A collection of components, hooks, and utilities for mini apps. Focus: consistent UX across Farcaster clients where no standards exist.
Website: https://hellno-mini-app-ui.vercel.app
Website: https://hellno-mini-app-ui.vercel.app
Install the most popular components:
# NFT display and minting
pnpm dlx shadcn@latest add https://hellno-mini-app-ui.vercel.app/r/nft-card.json
pnpm dlx shadcn@latest add https://hellno-mini-app-ui.vercel.app/r/nft-mint-flow.json
# User search (Farcaster, ENS, addresses)
pnpm dlx shadcn@latest add https://hellno-mini-app-ui.vercel.app/r/onchain-user-search.json
# Payment button
pnpm dlx shadcn@latest add https://hellno-mini-app-ui.vercel.app/r/daimo-pay-transfer-button.jsonpnpm dlx shadcn@latest add https://hellno-mini-app-ui.vercel.app/r/[component-name].jsonInstall all components with a single command:
# Default: Skip existing files
curl -sSL https://hellno-mini-app-ui.vercel.app/r/install-all.sh | bash
# Force overwrite existing files
curl -sSL https://hellno-mini-app-ui.vercel.app/r/install-all-overwrite.sh | bashWhen developing the registry locally:
# Start the registry locally first (in mini-app-ui repo)
pnpm dev
# Install individual components from localhost
pnpm dlx shadcn@latest add http://localhost:3000/r/nft-card.json --yes
# Note: install-all.sh may reference production URLs in dependencies
# For local development, install components individually90/10 Principle: Maximum impact with minimum effort. Start with the simplest solution that covers 90% of use cases.
-
Avoid Over-Engineering
- Don't create abstractions that "no one will use separately"
- Keep components self-contained rather than splitting into many files
- Example: NFTCard should include its own data fetching, not require separate hooks
-
LLM-Friendly Interfaces
- Use flat props, avoid nested objects
- Make each prop's purpose obvious from its name
- Example:
showTitle={true}notdisplay={{ title: true }}
-
Clear Component Boundaries
- Each component should have a single, clear responsibility
- NFTCard: Display only (no actions)
- NFTMintButton: Action only (no preview)
- NFTMintFlow: Combined experience
-
Production-Ready Patterns
// Always validate external data if (!Array.isArray(data) || data.length !== 2) { throw new Error("Invalid response structure"); } // Prevent race conditions const abortController = new AbortController(); useEffect(() => { return () => abortController.abort(); }, [deps]); // Use reliable infrastructure const transport = process.env.NEXT_PUBLIC_ALCHEMY_KEY ? http(`https://${alchemyUrl}.g.alchemy.com/v2/${key}`) : http(); // fallback
-
Eliminate Duplication
- Create shared libraries for common patterns
/lib/chains.ts- Chain configurations/lib/nft-standards.ts- ABIs and utilities/lib/nft-metadata-utils.ts- NFT metadata fetching with comprehensive fallbacks
- Manifold:
0x612b60c14ea517e1a538aee7b443e014a95de2d0 - Zora:
0x7c2668BD0D3c050703CEcC956C11Bd520c26f7d4 - Base:
0xba5e05cb26b78eda3a2f8e3b3814726305dcac83
Working with a custom shadcn registry has unique challenges:
-
Import Path Requirements
- All imports MUST use
@/registry/mini-app/...format - The shadcn CLI transforms these paths during installation
- Relative imports (
./lib/types) will fail after installation - Even imports within the same component's lib folder need full paths
- All imports MUST use
-
File Naming Constraints
- Avoid
page.tsx- it installs tosrc/components/page.tsx(wrong location) - Use descriptive names that match the component:
nft-mint-flow.tsx - Main component file should match the folder name
- Avoid
-
Multi-File Component Structure
blocks/my-component/ ├── my-component.tsx # Main component (not page.tsx!) ├── my-component-part.tsx # Sub-components └── lib/ # Supporting files ├── types.ts └── utils.ts -
Registry Dependencies
- Components can depend on other registry items via
registryDependencies - Shared code should go in top-level
/libfolder - Import shared code with
@/registry/mini-app/lib/...
- Components can depend on other registry items via
-
Installation Behavior
- All files in the component folder get installed
- Directory structure is preserved
- The shadcn CLI handles path transformations
- Test installation in a separate project to verify paths
⸻
registry/mini-app/<name>/<name>.tsx
Add new entry in items:
{
"name": "<name>",
"type": "registry:block",
"title": "<Readable title>",
"description": "<Short pitch>",
"files": [
{ "path": "registry/mini-app/<name>/<name>.tsx", "type": "registry:component" },
{ "path": "registry/mini-app/<name>/use-<name>.ts", "type": "registry:hook" }
],
"dependencies": ["@radix-ui/react-slot", "lucide-react"],
"registryDependencies": []
}The repo runs a Husky pre-commit hook:
pnpm lint # ESLint + Prettier pnpm registry:build # shadcn build ➜ public/r
The hook lints, generates fresh public/r/*.json, and blocks the commit on failure.
Vercel auto-deploys; the CLI fetches from https://hellno-mini-app-ui.vercel.app/r.
⸻
MIT