Skip to content

factor1/create-f1-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

create-f1-app

Interactive CLI to scaffold Factor1 headless CMS starter projects.

Combines your choice of CMS, frontend framework, hosting provider, and optional tooling into a ready-to-deploy project in seconds.

Supported stack options:

Layer Options
CMS Sanity, Storyblok, DatoCMS, Contentful, or None (local data)
Frontend Next.js 16, Astro 6
Hosting Vercel, Netlify, Cloudflare Pages, Self-hosted VPS
Package manager Bun, npm, pnpm, Yarn
Extras See optional features below

Usage

From npm (recommended)

No install required — use your package manager's create command:

# Bun
bun create f1-app

# npm
npm create f1-app@latest

# pnpm
pnpm create f1-app@latest

# Yarn
yarn create f1-app

The CLI walks you through all choices interactively and scaffolds the project into a new directory.

What gets generated

After scaffolding, your project includes:

  • Framework boilerplate wired to your chosen CMS (typed client, queries, env vars) — or, with None, a typed local content module at src/lib/content.ts and no CMS dependencies
  • Hosting config ready to deploy (vercel.json, netlify.toml, wrangler.toml, or a Dockerfile)
  • A .env.example (or .env if you entered credentials during setup) with all required keys
  • Any optional tooling you selected, fully configured
  • A landing page (src/app/page.tsx or src/pages/index.astro) with a live style guide for your chosen UI library and CMS-specific rendering patterns (PortableText, StoryblokComponent, StructuredText, documentToReactComponents) — or, with None, a working homepage that renders your local content

From a cloned repo

git clone https://github.com/factor1/create-f1-app.git
cd create-f1-app
bun install       # or npm install
bun run build     # compiles TypeScript → dist/
node dist/index.js

To run in watch mode during development:

bun run dev

Optional features

All extras are toggle-able at the prompt. Features marked Next.js only are silently skipped when Astro is selected.

Feature What it adds
Tailwind CSS v4 CSS-first config, no tailwind.config.ts required
shadcn/ui Installs packages, writes components.json, seeds CSS token set. Optionally adds Button, Card, and Input starter components. Auto-enables Tailwind.
Headless UI @headlessui/react v2 — unstyled, fully accessible primitives (Dialog, Disclosure, Menu, Tab, etc.). Pairs naturally with Tailwind.
Radix UI Low-level unstyled primitives (@radix-ui/react-slot, Label, VisuallyHidden). Full styling control without an opinionated design system.
Mantine @mantine/core + @mantine/hooks — fully styled component library with 100+ components and its own CSS-in-JS theming. Creates src/app/providers.tsx and patches layout.tsx. Conflicts with Tailwind — the CLI prompts you to keep one.
ESLint Flat config (eslint.config.mjs) with framework-aware rules (Next.js or Astro)
Prettier .prettierrc + .prettierignore. Appends eslint-config-prettier when ESLint is also selected
TypeScript strict mode noUncheckedIndexedAccess + exactOptionalPropertyTypes added to tsconfig.json
Env var validation Next.js: src/env.ts via @t3-oss/env-nextjs + zod. Astro: astro:env schema in astro.config.mjs. Covers all CMS and selected-feature env vars
CMS image helpers Per-CMS typed utility in src/lib/{cms}/image.ts (Sanity urlFor, Storyblok transform, DatoCMS srcset builder, Contentful Image API)
Revalidation webhook POST /api/revalidate — validates x-revalidate-secret header, accepts { path } or { tag } body to call revalidatePath/revalidateTag. Next.js only
Draft/preview mode GET /api/preview?secret=…&slug=… enables Next.js draft mode and redirects. GET /api/disable-draft exits preview. Next.js only
Sentry @sentry/nextjs or @sentry/astro, SDK config files, withSentryConfig wrapper on next.config.ts, SENTRY_DSN env var
Analytics Sub-prompt for provider: Vercel Analytics, PostHog, or Plausible. Injected into the root layout for both frameworks
Vitest Unit test config with framework plugin (@vitejs/plugin-react for Next.js), example test and setup file
Playwright E2E config wired to the correct dev port (3000/4321) and package manager run command
GitHub Actions CI workflow that installs deps and runs whichever of lint, format check, unit tests, and E2E tests you selected
Dependabot .github/dependabot.yml — weekly grouped dependency updates (production + development)
MCP config .cursor/mcp.json + .mcp.json — wires your CMS's MCP server with env var references for AI editor integration
Storybook Component explorer (experimental with Astro — prompts before enabling)

Analytics providers

Provider What's generated
Vercel Analytics <Analytics /> injected into layout. @vercel/analytics package
PostHog src/app/providers.tsx client component wrapping the app. posthog-js package. POSTHOG_KEY + POSTHOG_HOST env vars
Plausible Script tag injected into layout <head>. No additional packages. PLAUSIBLE_DOMAIN env var

shadcn/ui note

Selecting shadcn installs the packages, writes components.json, and adds the CSS variable token set to your global stylesheet. It does not pre-generate component files by default — add components after init with:

npx shadcn add button
npx shadcn add card input dialog   # etc.

Optionally, the CLI can scaffold Button, Card, and Input starter files for you — just answer Yes when prompted. Badge, Label, and Textarea components are always generated (used by the landing page style guide).

Mantine note

Mantine uses its own CSS-in-JS theming system and does not work well alongside Tailwind. If you select both, the CLI will ask which to keep. Mantine setup creates:

  • src/app/providers.tsx'use client' wrapper with MantineProvider
  • Patches layout.tsx to import and wrap {children} with <Providers>
  • postcss.config.mjs with postcss-preset-mantine and postcss-simple-vars

CMS credentials

The CLI optionally prompts for CMS API keys during setup. If you skip this step, all required keys are written to .env.example so you can fill them in later:

cp .env.example .env
# then fill in your values

Env var reference

CMS Variable(s)
Sanity (Next.js) NEXT_PUBLIC_SANITY_PROJECT_ID, NEXT_PUBLIC_SANITY_DATASET, SANITY_API_TOKEN
Sanity (Astro) PUBLIC_SANITY_PROJECT_ID, PUBLIC_SANITY_DATASET, SANITY_API_TOKEN
Storyblok STORYBLOK_ACCESS_TOKEN
DatoCMS DATOCMS_API_TOKEN
Contentful CONTENTFUL_SPACE_ID, CONTENTFUL_ACCESS_TOKEN, CONTENTFUL_PREVIEW_TOKEN
None (local data) none — content lives in src/lib/content.ts

Feature-specific vars are added automatically when the relevant feature is selected (e.g. REVALIDATE_SECRET, PREVIEW_SECRET, NEXT_PUBLIC_SENTRY_DSN).

CMS renderer packages

Rich-text renderer packages are added automatically based on your CMS and framework:

CMS Next.js Astro
Sanity next-sanity (includes @portabletext/react) @sanity/client · @portabletext/react added when a React UI lib is selected
Storyblok @storyblok/react @storyblok/astro
DatoCMS @datocms/cda-client · graphql-request · react-datocms @datocms/cda-client · graphql-request · react-datocms added when a React UI lib is selected
Contentful contentful · @contentful/rich-text-react-renderer · @contentful/rich-text-types contentful · @contentful/rich-text-html-renderer · @contentful/rich-text-types

No CMS (local data)

Choosing None scaffolds a project with no CMS at all. Content lives in a typed module at src/lib/content.ts (a site object and a posts array), and the generated homepage renders it directly — so the project runs offline, out of the box, with no API keys or extra dependencies. When you're ready to go headless, swap the exports in content.ts for a CMS client; the rest of the app keeps working as long as the shapes match.

Because they have no meaning without a CMS, these features are automatically skipped when None is selected: CMS image helpers, MCP config, draft/preview mode, and the revalidation webhook. The CMS credentials prompt is also skipped.


Project structure

src/
  index.ts      # CLI entry point (--version, --help)
  cli.ts        # Interactive prompt flow
  scaffold.ts   # Template layer composition and programmatic file generation
  install.ts    # Package manager install + git init
  utils.ts      # File walking, deep merge, template variable substitution
  config.ts     # CMS environment variable mappings
  types.ts      # Shared TypeScript types
templates/
  base/                               # Files copied into every project
  frontend/{nextjs,astro}/            # Framework base files
  cms/{sanity,storyblok,datocms,contentful,none}/{nextjs,astro}/
  hosting/{vercel,netlify,cloudflare,vps}/{nextjs,astro}/
  optional/{tailwind,shadcn,shadcn-components,eslint,prettier,
            vitest,playwright,github-actions,storybook,
            revalidation,preview,sentry,dependabot}/

Templates are composed in layers — later layers override earlier ones. Files prefixed with _ are renamed to . on copy (e.g. _gitignore.gitignore). Files named _package.json are deep-merged into the final package.json. Some features (analytics, env-validation, cms-images, strict-ts, landing page) are generated entirely programmatically based on the selected CMS and framework.

Headless UI, Radix UI, and Mantine are injected programmatically (no template directory) — packages are merged into package.json and framework config is patched as needed.


Publishing to npm

  1. Bump the version in package.json following semver:

    npm version patch   # or minor / major
  2. Build the distributable:

    bun run build
  3. Verify what will be published (should include dist/ and templates/):

    npm pack --dry-run
  4. Publish:

    npm publish --access public

    Requires being logged in to npm (npm login) with publish access to the create-f1-app package.

  5. Push the version tag created by npm version:

    git push && git push --tags

About

CLI tool for creating headless CMS apps

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages