Ship a real Nostr web app without spending your first week rebuilding SSR, auth, SEO, onboarding, and deployment plumbing.
This template gives you the hard parts up front: server-rendered pages that still feel live, session-aware client UX, shareable previews, reusable NDK primitives, and a deployment path that already makes sense.
The included UI uses profiles, notes, articles, comments, and highlights to show the stack in action. Those are examples of what the foundation supports, not the limit of what you can build with it.
.well-known/nostr.json@ndk/svelte primitives already wired into the app structureNDKSvelte is the right tool for client subscriptions, live feeds, and session-aware UI. It is not the right thing to depend on for social crawlers. Crawlers only see the HTML returned by the server, so preview-critical routes need to fetch their own Nostr data in +page.server.ts and emit SEO tags there.
This template makes that explicit instead of hiding it behind one cross-environment singleton.
The starter now behaves like a real @ndk/svelte jsrepo consumer:
jsrepo.config.ts points at @ndk/sveltesrc/lib/ndk/*ui/user primitiveTo add more registry items into the same structure:
bunx jsrepo add ui/user
bunx jsrepo add components/session-switcher
/ shows a publication-style front page seeded with long-form articles/profile/[identifier] SSR-fetches an author profile and recent articles/note/[id] SSR-fetches an article or note and author metadataBoth SSR routes return seo data that the root layout renders through SeoHead.svelte.
bun install
bun run dev
Set relays with:
PUBLIC_NOSTR_RELAYS=wss://relay.damus.io,wss://purplepag.es,wss://relay.primal.net
If omitted, the template uses those three relays by default.
To let SSR loads serve Nostr events from a durable Vercel-friendly cache, add Upstash Redis REST credentials:
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
Vercel KV's KV_REST_API_URL and KV_REST_API_TOKEN names are also supported.
The SSR cache uses @nostr-dev-kit/cache-upstash, stores under the
sveltekit-vercel-ndk:ssr:v1 namespace by default, and falls back to relay
fetches when the cache is missing or not configured.
Optional cache tuning:
NDK_SSR_CACHE_NAMESPACE=
NDK_SSR_CACHE_TTL_SECONDS=3600
To enable managed NIP-05 registration in onboarding, add:
PUBLIC_NIP05_DOMAIN=your-domain.com
The value can be a bare domain or a full URL. If set, onboarding shows an optional handle field, checks [email protected] availability, and the app serves /.well-known/nostr.json.
For durable registrations on Vercel, also provide a writable KV-compatible store:
KV_REST_API_URL=
KV_REST_API_TOKEN=
Without those two variables, the template falls back to an in-memory registry that is only suitable for local development.
SvelteKit.PUBLIC_NOSTR_RELAYS if you want custom relays.UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN for durable SSR NDK caching. Vercel KV's KV_REST_API_URL and KV_REST_API_TOKEN work too.PUBLIC_NIP05_DOMAIN if you want the template to issue handles for your domain.KV_REST_API_URL and KV_REST_API_TOKEN if you want those handle registrations to persist across instances and did not already add them for SSR caching.No custom vercel.json is required for the base template.
src/lib/ndk/client.ts: browser NDKSvelte instance with session persistencesrc/lib/ndk/ui/: registry-installed UI primitives from @ndk/sveltesrc/lib/ndk/builders/: registry-installed builders used by those primitivessrc/lib/ndk/utils/ndk/: the NDK context helper expected by registry itemssrc/lib/server/nostr.ts: server-only NDK helpers for SSR loadssrc/lib/seo.ts: preview metadata builderssrc/lib/components/SeoHead.svelte: canonical, OG, and Twitter tagsjsrepo.config.ts: consumer config for adding more registry items with jsrepoThis template ships with a stable default OG image in static/og-default.png plus route-specific
titles and descriptions. If you want fully dynamic per-note images, layer that on top of the same
SSR metadata flow rather than moving preview generation into client code.