A demo toolkit built with Next.js 16, React 19, Prisma, Tailwind CSS, and shadcn/ui (built on Base UI). Includes a component showcase, a slide deck powered by nextjs-slides, and modern patterns for building interactive demos.
Uses cacheComponents for performance and modern Async React patterns for declarative async coordination.
bun install
bun run devOpen http://localhost:3000 in your browser.
This project uses SQLite with a local database file (dev.db). No environment configuration needed.
bun run prisma.generate # Generate the Prisma client
bun run prisma.push # Push schema to database
bun run prisma.seed # Seed initial data
bun run prisma.studio # View data in Prisma StudioUsing Prisma Postgres instead: Change the provider in prisma/schema.prisma to postgresql, update db.ts to use @prisma/adapter-pg, and set your connection string in .env:
DATABASE_URL="postgres://..."app/ # Pages and layouts
_components/ # Route-local components
slides/ # Slide deck (uses nextjs-slides package)
components/
design/ # Design system components with Action props
ui/ # shadcn/ui primitives
data/
actions/ # Server Actions
queries/ # Data fetching with cache()
lib/
fetcher.ts # Shared SWR fetcher
- components/ui — shadcn/ui components. Add with
bunx shadcn@latest add <component-name> - components/design — Components that expose Action props and handle async coordination internally Every page folder should contain everything it needs. Components and functions live at the nearest shared space in the hierarchy.
Naming: PascalCase for components, kebab-case for files/folders, camelCase for functions/hooks. Suffix transition-based functions with "Action".
- Fetching data — Queries in
data/queries/, wrapped withcache(). Await in Server Components directly, or pass the promise to a client component and unwrap withuse(). Use SWR withlib/fetcher.tsfor dependent or interactive client-side fetches (e.g. cascading filter options). - Mutating data — Server Actions in
data/actions/with"use server". Invalidate withrevalidateTag(). UseuseTransition+useOptimisticfor pending state and instant feedback. - Navigation — Wrap route changes in
useTransitionto getisPendingfor loading UI. - Caching — Add
"use cache"withcacheLife()andcacheTag()to pages, components, or functions. Invalidate withupdateTag()(Server Actions, read-your-own-writes) orrevalidateTag()(Route Handlers, webhooks). - Errors —
error.tsxfor boundaries,not-found.tsx+notFound()for 404s. Errors thrown inside transitions automatically reach the nearest error boundary.
Cache Components: Uses cacheComponents: true to statically render server components that don't access dynamic data. Keep pages non-async and push dynamic data access into <Suspense> boundaries to maximize the static shell. Use "use cache" with cacheLife() and cacheTag() to explicitly cache; invalidate with updateTag (Server Actions) or revalidateTag (Route Handlers).
Async React: Replace manual isLoading/isError state with React 19's coordination primitives — useTransition for tracking async work, useOptimistic for instant feedback, Suspense for loading boundaries, and use() for reading promises during render. See AGENTS.md for detailed patterns and examples.
Uses ESLint and Prettier with format-on-save in VS Code. Configuration in eslint.config.mjs and .prettierrc. Open the .code-workspace file to ensure correct extensions are set.
bun run buildDeploy to Vercel for the easiest experience. Use a production database instead of SQLite.
See the Next.js deployment docs for more details.
Uses nextjs-slides for composable presentations at /slides. Add slides by composing primitives in app/slides/slides.tsx. See the package documentation for available primitives and props.