Skip to main content
StormyCMS

Boilerplate Tour

stormy init scaffolds the official Next.js boilerplate — both a working starter and a reference implementation of the @stormycms/* packages.

Structure

my-stormy-site/ # pnpm + Turborepo monorepo
├── apps/
│ ├── site/ # Public CMS-rendered site (localhost:3000)
│ └── admin/ # Your admin editor (localhost:4000)
└── packages/
├── components/ # Shared components + CMS export map (index.ts)
└── types/ # Shared ambient type declarations

apps/site — the public site

A catch-all route (app/(pages)/[...slug]/page.tsx) resolves every URL to a CMS page via getPageBySlug, maps it with mapPageData, and renders it with LayoutTree + ComponentTree using the shared export map. generateMetadata pulls SEO metadata from the page. Missing slugs redirect to /404. See Examples for the full code.

Needs only NEXT_PUBLIC_SITE_URL, STORMY_CMS_CLIENT_ID, STORMY_CMS_CLIENT_SECRET.

apps/admin — your editor

The customer-owned editing application: page editor, layout editor, and media library. Auth is wired through createNextAuthHandlers at /api/auth/stormy (GitHub/Google OAuth), and the whole app sits inside StormyServerProvider so the session, current site, and site list are available everywhere via useStormy.

Additional env: STORMY_CMS_REDIRECT_URL (OAuth callback), NEXT_PUBLIC_PREVIEW_SITE_URL (points at the public site for previews), and S3_* variables for media uploads to your own bucket.

Admin routes

RoutePurpose
/Dashboard
/pagesList and create pages
/pages/[id]Page editor (CMSPageProvider + PageEditor)
/layoutsList and create layouts
/layouts/[id]Layout editor (CMSLayoutProvider + LayoutEditor)
/mediaMedia library and upload UI
/api/auth/stormyOAuth handler from @stormycms/next
/api/mediaLocal REST API for listing, uploading, updating, and deleting media (proxies to your S3 bucket and the Stormy CMS media API)

Media API (/api/media)

The admin exposes a small local REST layer so the browser can upload files without exposing S3 credentials to the client:

  • GET /api/media — list media items from the CMS (limited to 100).
  • POST /api/media — upload a file to S3 and create a Media record in Stormy CMS.
  • PUT /api/media — update a media item's metadata.
  • DELETE /api/media?id=... — delete the media record and the underlying S3 object.

It uses a request-scoped StormyCMSClient with auto-minted JWT via createStormyCMSClient().

packages/components — the shared schema

index.ts is the CMS export map (Components & Schema). The starter inventory: semantic regions (Region, RegionHeader, RegionMain, RegionAside, RegionFooter), Group, Section, Heading, Text, RichText, Image, Navigation, SiteInfo, Card, CardGroup, Hero, CallToAction, and the built-in Outlet.

Add your own components under packages/components/src, register them in index.ts with withCMS, and they appear in the editor immediately. stormy generate can draft registrations from existing component props.

Commands

Terminal window
pnpm dev # run both applications
pnpm quality:light # lint, formatting, and TypeScript checks
pnpm test # unit tests
pnpm test:e2e:install # install Playwright browsers once
pnpm test:e2e # browser tests
pnpm build # production builds

E2E tests

The admin app has a Playwright suite in apps/admin/e2e/. Tests run against a production build on port 4010 (override with E2E_PORT). For local automation, the test harness can bypass real OAuth by setting E2E_ALLOW_COOKIE_BYPASS=true and sending a cookie named e2e_bypass_auth=true.

Deployment

Deploy apps/site and apps/admin as two separate Next.js applications. Configure each from its .env.example; never commit .env files. Use a restricted S3 credential for media in production. Run pnpm quality before release.

Last updated: 7/9/26, 6:42 AM

StormyCMSThe headless CMS where you own the management panel
Community
StormyCMS