The official landing surface for decentralized apps built on Lightchain — a curated, community-extensible catalogue at hub.lightchain.ai. DeFi, NFTs, gaming, developer tools, AI agents, and infrastructure — anyone can submit a dApp via pull request.
- Submit a dApp ← if you're here to list your app, start here
- What this repo is
- Tech stack
- Project structure
- Local development
- Production build
- Adding the official badge to a dApp
- How nav and footer are populated
- Image and asset guidelines
- Tag taxonomy
- Security model
- Reporting bugs
- Contributing code
- License
Add your dApp to the hub by opening a pull request that creates a single JSON file. No code changes required.
Name it dapp-<unique-id>.json. The id should be short, lowercase, hyphen-separated, and globally unique within the repo (e.g. dapp-acme-swap, not dapp-101).
{
"id": "dapp-acme-swap",
"name": "Acme Swap",
"description": "One- or two-sentence description shown on the card. Keep it under 160 characters for clean truncation.",
"tags": ["DEFI", "SWAP", "MAINNET"],
"iconSrc": "/images/dapp-item-logo/acme-swap.png",
"imageSrc": "/images/dapp-item-thumb/acme-swap.png",
"externalUrl": "https://acme-swap.example/"
}Place your dApp's two images under public/images/:
| Field | Path | Size | Format |
|---|---|---|---|
iconSrc |
public/images/dapp-item-logo/ |
256×256 px (1:1) | PNG with transparency, < 100 KB |
imageSrc |
public/images/dapp-item-thumb/ |
800×450 px (16:9) | PNG or JPG, < 250 KB |
Don't use external URLs for iconSrc / imageSrc — they get blocked by our next.config.ts image policy. Host the files in this repo via your PR.
| Field | Type | Notes |
|---|---|---|
id |
string | Globally unique within the repo. Prefix with dapp-. Used in URLs. |
name |
string | The dApp's display name. 2-40 chars. |
description |
string | Card description. Keep under 160 chars; longer text is truncated to 3 lines in the UI. |
tags |
string[] | 1-3 tags from the tag taxonomy. All uppercase. |
iconSrc |
string | Path beginning with /images/dapp-item-logo/. Repo-relative only. |
imageSrc |
string | Path beginning with /images/dapp-item-thumb/. Repo-relative only. |
externalUrl |
string | Full HTTPS URL of your dApp's homepage. http://, javascript:, and other schemes are rejected by the loader. |
git checkout -b add-dapp-acme-swap
git add constants/additionalDapps/dapp-acme-swap.json public/images/dapp-item-logo/acme-swap.png public/images/dapp-item-thumb/acme-swap.png
git commit -m "feat: add Acme Swap to dApp hub"
git push origin add-dapp-acme-swapThen open a PR against main and fill in the PR template. A maintainer will review, primarily checking that:
- The URL resolves to a working, on-Lightchain dApp (not a phishing clone)
- The images render at the right aspect ratio
- The tags are from the canonical taxonomy
- Required fields are present
Most well-formed PRs are merged within 1-3 business days.
Within a few minutes of merge:
- A Vercel preview build runs against the PR for the maintainer to review.
- On merge to
main, the production site at hub.lightchain.ai rebuilds automatically. - Your dApp appears in the "showing all" section, sorted to the top (newest first) for the first few days.
If your dApp is later deprecated or you change domains, open another PR editing the same dapp-*.json file.
lcai-dApp-hub is the source code for hub.lightchain.ai, the public catalogue of dApps in the Lightchain ecosystem. It is:
- A Next.js 16 App Router application (TypeScript, React 19, Tailwind 4)
- Static-by-default — every dApp page is statically rendered at build time
- Self-extending — community PRs that drop JSON files under
constants/additionalDapps/cause the next build to pick them up with no code changes - Open source under MIT
It is not:
- A wallet, a swap, a bridge, or any other dApp — those are listed on the hub, not built into it
- A directory of arbitrary websites — listings must run on Lightchain or be operator tools for builders on Lightchain
- A DeFi yield aggregator, a price tracker, or a token list
For a full list of what's currently in the hub, see constants/dapps.ts (official entries maintained by the Lightchain team) and constants/additionalDapps/ (community submissions).
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 (App Router) | RSC + static export for fast, SEO-friendly catalogue pages. |
| Language | TypeScript 5 (strict) | Catches dApp schema mistakes at build, not runtime. |
| UI primitives | shadcn + Base UI + Radix UI | Accessible, themable, low-magic. |
| Styling | Tailwind CSS 4 | Utility-first; matches the rest of the Lightchain frontend stack. |
| Icons | lucide-react | Tree-shakable, consistent visual language. |
| 3D background | three.js + @react-three/fiber + postprocessing | The particle-field hero animation in <Antigravity />. |
| Toasts | sonner | Used by the "report" + "share" dialogs. |
| Slugs | slugify | Generates ${name}-${id} URL slugs. |
| Package manager | pnpm 9+ (workspace) | Single repo, scoped workspaces if needed in future. |
| Deployment | Vercel | Auto-deploys main to production, every PR to a preview URL. |
.
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout. Fetches nav + footer config on every request (revalidate=3600).
│ ├── page.tsx # Home page. Loads built-in + community dApps and renders the catalogue.
│ ├── globals.css # Tailwind 4 base + design tokens.
│ └── [slug]/page.tsx # dApp detail page. (See BUGS.md - currently a hardcoded stub.)
│
├── components/
│ ├── dapp-card/ # Card variants + safety dialog gating external links
│ │ ├── DappCard.tsx
│ │ ├── DappCardMini.tsx
│ │ ├── DappSafetyDialog.tsx
│ │ └── NoDAppBox.tsx # Empty-state illustration.
│ ├── dapp-details/ # Detail page sidebar + content (currently hardcoded - see BUGS.md)
│ ├── home/ # Hero, search field, filters, sorting, trending, empty state
│ ├── header/, footer/ # Top nav + site footer, populated from docs.lightchain.ai JSON
│ ├── icons/ # Social media SVG icon components
│ └── ui/ # shadcn-style primitives (Button, Badge, etc.) + 3D bg
│
├── constants/
│ ├── dapps.ts # Built-in (Lightchain-team-maintained) dApps. Get the OFFICIAL badge.
│ └── additionalDapps/
│ └── *.json # Community submissions. One file per dApp.
│
├── hooks/ # useShare, useSwiperEdgeState
├── lib/
│ ├── footer/ # Footer config fetcher + types + icon maps
│ ├── nav/ # Nav config fetcher + types + icon maps + resolveTarget
│ ├── loadAdditionalDapps.ts # Reads + validates community JSON at build time
│ └── utils.ts # cn() (tailwind merge), generateSlugWithId()
│
├── public/
│ ├── images/
│ │ ├── dapp-item-logo/ # 256×256 PNG per dApp
│ │ ├── dapp-item-thumb/ # 800×450 PNG/JPG per dApp
│ │ ├── details-content-image/, bg/, icons/, logo/ # Site chrome
│ └── *.png, *.svg, *.ico # Favicons + OG assets
│
├── next.config.ts # Image policy + headers + Turbopack config
├── tsconfig.json # @/* alias + strict mode
├── eslint.config.mjs # Next core-web-vitals + TS rules
├── package.json
├── README.md # ← you are here
└── BUGS.md # Known issues + suggested fixes (see below)
Prerequisites:
- Node.js 20+ (the Next.js 16 release line requires 20.x or newer)
- pnpm 9+ — the lockfile is
pnpm-lock.yaml, sonpm/yarnwill produce different installs and may break
git clone https://github.com/lightchain-protocol/lcai-dApp-hub.git
cd lcai-dApp-hub
pnpm install
pnpm devOpen http://localhost:3000.
Hot reload + RSC fast refresh are on. Edits to constants/dapps.ts or any constants/additionalDapps/*.json show up immediately. Edits to app/layout.tsx trigger a full reload (because it fetches nav/footer config at module load).
| Command | What it does |
|---|---|
pnpm dev |
Start the Next.js dev server on :3000 (with Turbopack) |
pnpm build |
Production build into .next/ |
pnpm start |
Start the production server (must build first) |
pnpm lint |
Run ESLint with Next's core-web-vitals + TypeScript rules |
There is no test runner configured at the time of writing. See BUGS.md for the recommendation to add Vitest + Playwright.
pnpm devfails at module load with "Failed to fetch nav config: 502" —app/layout.tsxfetches nav + footer JSON fromdocs.lightchain.aion every request. If those URLs are unreachable from your network, the layout throws. Workaround: runpnpm devonce with a stable connection so Next caches the fetch (revalidate=3600), or mock the fetches in alib/mock.tsfor offline work.pnpm buildfails because Tailwind 4 isn't finding the v4 plugin — make sure you're using pnpm 9+ and that@tailwindcss/postcssresolved correctly.rm -rf node_modules && pnpm installusually fixes it.Module not found: @/...— the@/*alias is defined intsconfig.json. If your editor isn't picking it up, restart the TS server.
pnpm build
pnpm startThe build emits a fully static catalogue (all constants/additionalDapps/*.json files are read at build time). To regenerate after a new dApp PR merges, simply redeploy — there is no separate CMS or admin panel.
The main branch auto-deploys to https://hub.lightchain.ai. PRs get preview URLs via the GitHub integration.
For a self-hosted alternative:
pnpm build
# Serve .next/ via any Next.js-compatible host (Vercel, Cloudflare Pages, Netlify, Fly.io, etc.)The app has no runtime backend dependencies beyond:
docs.lightchain.ai/nav-config.jsondocs.lightchain.ai/footer-config.json
both fetched at first request and cached for 1 hour. If you fork for a different network, point those URLs at your own static JSON.
The card UI shows a small "OFFICIAL" badge next to a dApp's name if either added_by_team or powered_by_lightchain is true. This flag cannot be set via a community PR — the loader (lib/loadAdditionalDapps.ts) strips it from any JSON submission and forces both fields to false.
To add an official entry, a maintainer adds it directly to constants/dapps.ts instead of constants/additionalDapps/. The full set of supported flags:
type DappCardProps = {
id: string | number;
name: string;
description: string;
tags: string[];
iconSrc: string;
imageSrc: string;
externalUrl?: string;
added_by_team?: boolean; // snake_case (preferred for new entries)
addedByTeam?: boolean; // camelCase (legacy alias - still read)
powered_by_lightchain?: boolean;
poweredByLightchain?: boolean;
};The two casings are read with addedByTeam ?? added_by_team so either works; new entries should pick one and stick with it.
Both the top navigation and the footer columns are NOT defined in this repo — they're fetched from JSON at https://docs.lightchain.ai/:
| Surface | URL | Fetched in | Revalidate |
|---|---|---|---|
| Top nav | https://docs.lightchain.ai/nav-config.json |
lib/nav/fetchNavConfig.ts |
1 hour |
| Footer | https://docs.lightchain.ai/footer-config.json |
lib/footer/fetchFooterConfig.ts |
1 hour |
This is by design — the nav and footer are shared across multiple Lightchain frontends (the hub, the docs site, the explorer, etc.) and a single source of truth keeps them in sync. To change them, update the JSON at docs.lightchain.ai (separate repo); the change will roll out to the hub within an hour.
Failure modes:
- If either fetch throws (network error, 5xx), the entire layout throws and the request returns 500. Worth adding a fallback — see BUGS.md.
Two reasons to keep images repo-local rather than external:
- Security: external image URLs would need to be allowlisted in
next.config.tsper host. We don't want submitters to silently load images from random origins. - Performance: Next.js Image optimizes local images at build time. External images need to be re-fetched and re-optimized on each cold request.
- Aspect ratio: 16:9 (
aspect-videoin Tailwind) - Recommended dimensions: 800×450 px or larger (will be downscaled, never upscaled)
- Format: PNG with transparency or JPEG
- File size: < 250 KB — compress with Squoosh or similar
- Subject: a screenshot, branded illustration, or feature collage. Avoid text-heavy thumbnails — at card size they're unreadable.
- Aspect ratio: 1:1
- Recommended dimensions: 256×256 px
- Format: PNG with transparent background
- File size: < 100 KB
- Safe area: leave ~20% padding around the logo so it doesn't touch the circle clip
- High contrast against both light and dark backgrounds (the card switches based on user theme)
If a maintainer adds detail-page screenshots, drop them under public/images/details-content-image/ at 1200×675 px (16:9). Reference them from the (currently-being-built) detail page component.
Use 1-3 tags per dApp, all UPPERCASE. The canonical set:
| Category | Tags |
|---|---|
| DeFi | DEFI, SWAP, LENDING, STAKING, YIELD |
| Liquidity | DEX, AMM, LP, LIQUIDITY |
| Bridges | BRIDGE, INTERCHAIN, LCAI |
| AI | AI, WORKERS, MODELS, INFERENCE, AGENTS |
| NFTs | NFT, MARKETPLACE, MINTING |
| Gaming | GAMING, GAMEFI, PLAY-TO-EARN |
| Social | SOCIAL, IDENTITY, DAO |
| Infra / Tools | INFRA, EXPLORER, ANALYTICS, FAUCET, IDE, DEVTOOLS, CONTRACTS, TOOLS, BUILDER |
| Networks | MAINNET, TESTNET |
If your dApp doesn't fit, propose a new tag in your PR description — we'd rather extend the taxonomy carefully than have it sprawl.
We treat the dApp hub as a trust-amplifying surface — listings are validated and badges signal provenance. The full threat model is in docs/security.md once that file lands; the high-level invariants are:
- Community submissions can NEVER claim the OFFICIAL badge.
loadAdditionalDapps.tsunconditionally stripsadded_by_teamandpowered_by_lightchainfrom JSON submissions, regardless of casing. Only entries inconstants/dapps.ts(which requires maintainer review) get the badge. - External URLs are gated by a safety dialog for non-official dApps. First-time clicks open the "Stay in Control" modal explaining wallet hygiene; users can opt out per-URL via
localStorage(lcai:dapp-safety-dismissed:${url}). - External links always use
target="_blank" rel="noopener noreferrer", preventing reverse-tabnabbing. - External URL scheme is validated to be
http:orhttps:only.javascript:,data:, etc. are rejected by the loader. - Images are repo-local —
next.config.tsdoes not allowlist any external image hosts, soiconSrc/imageSrccannot point off-domain.
If you find a way around any of these, please email security@lightchain.ai (private disclosure) rather than opening a public issue. See SECURITY.md once it lands.
For non-security bugs, open a GitHub issue using the bug report template. Please include:
- What you expected vs what actually happened
- A reproduction (link to the failing URL, paste relevant logs)
- Your browser + OS + viewport size
- Whether the issue blocks dApp discovery or is purely cosmetic
For known issues + their suggested fixes, see BUGS.md.
Beyond dApp submissions, code-level contributions are very welcome — see CONTRIBUTING.md.
Quick start for code PRs:
git clone https://github.com/lightchain-protocol/lcai-dApp-hub.git
cd lcai-dApp-hub
pnpm install
pnpm dev
# Make your changes…
pnpm lint
git checkout -b feat/short-description
git commit -m "feat: short description"
git push -u origin feat/short-description
gh pr create --fillSee BUGS.md for a prioritized list of things that could use a PR.
MIT. Use it however you like. No warranty.
Built by the Lightchain team and contributors. Special thanks to everyone who's submitted a dApp 🙏