Skip to content

Major maintenance update: App Router support, dependency bumps, website overhaul #271

@akd-io

Description

@akd-io

The project has been dormant since mid-2023. This issue tracks the full maintenance update to make create-next-stack current and relevant again.

Key decisions

  • Support both App Router (default) and Pages Router via --router flag
  • Update all plugin dependencies to latest major versions
  • Migrate website from Chakra UI to Mantine, Pages Router to App Router

Phase 0: Build tooling & infrastructure

  • TypeScript 4.9 -> 5.x (superseded by Phase 9: now ^6)
  • Turbo v1 -> v2 (pipeline -> tasks)
  • Prettier v2 -> v3
  • pnpm v8 -> v10
  • Husky v8 -> v9
  • oclif v2 -> v4
  • Node engines >=18 -> >=20
  • GitHub Actions versions (setup-node@v4, checkout@v4, pnpm/action-setup@v4) (superseded by Phase 9: @v6 / @v6 / @v5)

Phase 1: Slot system refactoring + router flag

  • Add --router flag (app/pages, default app)
  • Refactor Plugin type: rename slots.app -> slots.pagesApp, add slots.appLayout for App Router
  • App Router providers.tsx pattern (separate 'use client' wrapper component)
  • New generators: app/layout.tsx, app/providers.tsx, app/page.tsx
  • Conditional file generation based on router flag
  • Update Next.js plugin: create-next-app 13.2.3 -> 15.x, update CNA flags

Phase 2: Plugin updates

Simple bumps

  • framer-motion -> motion ^12 (package renamed, imports from motion/react)
  • react-icons ^4 -> ^5
  • prisma ^4 -> ^6 (v7 too breaking; see Finish Prisma support #270) (Phase 9 re-evaluated v7 — still too breaking; left at ^6)
  • vercel ^30 -> ^50, netlify ^15 -> ^24 (superseded by Phase 9: vercel ^51, netlify ^25)
  • github-actions template update (checkout@v4, setup-node@v4, pnpm/action-setup@v4) (superseded by Phase 9)
  • react-query ^4 -> ^5 + App Router provider slots
  • sass, next-plausible, react-hook-form, formik version checks + App Router slots where needed (next-plausible slipped — caught and bumped to ^4 in Phase 9)

Breaking changes

  • Tailwind CSS v3 -> v4 (CSS-based config via @import "tailwindcss", drop tailwind.config.js/autoprefixer, use @tailwindcss/postcss)
  • Chakra UI v2 -> v3 (still needs @emotion/react, drops @emotion/styled/framer-motion/@chakra-ui/icons, uses createSystem/defineConfig, needs next-themes)
  • Mantine v6 -> v8 (dropped Emotion, uses PostCSS with postcss-preset-mantine, CSS imports, createTheme). Hybrid App Router approach: layout slots for CSS import/mantineHtmlProps/ColorSchemeScript, provider slots for MantineProvider (superseded by Phase 9: now ^9)
  • Material UI v5 -> v7 + @mui/material-nextjs for App Router (AppRouterCacheProvider) (superseded by Phase 9: now ^9)
  • Styled Components v5 -> v6 (built-in TS types, drop @types/styled-components, add App Router registry via useServerInsertedHTML)
  • Emotion: add App Router provider slots (CacheProvider + useServerInsertedHTML), add README todo about CSS-in-JS Client Component requirement
  • CSS Modules: App Router layout import (done in Phase 1)

Infrastructure

  • Add postcssConfig slot to Plugin type (like nextConfig slots) for PostCSS plugin merging (Mantine + Tailwind coexistence)
  • Migrate next.config.js -> next.config.ts generation

Formatting/hooks

  • Prettier ^2 -> ^3, eslint-config-prettier ^8 -> ^10
  • Drop mrm/mrm-task-lint-staged, use Husky v9 (npx husky init) + lint-staged ^16

Phase 3: Validation rules

  • Remove "Mantine requires Emotion" constraint
  • Keep "Chakra requires Emotion" (v3 still uses Emotion)
  • Remove "Chakra requires Framer Motion" constraint
  • Keep "Material UI requires Emotion"
  • Update flag descriptions to match (Chakra, Mantine)

Phase 4: E2E tests

  • Add --router=pages test variants (pages-router-emotion-all-flags, pages-router-tailwind-css-only)
  • Add --prisma and --github-actions to all-flags tests
  • Add Mantine-without-Emotion test (tailwind-css-with-mantine)
  • Remove obsolete invalid-input cases (mantineWithoutEmotion, chakraWithoutFramerMotion)
  • Update chakraWithoutEmotion test (remove --framer-motion from args)

Bugs found and fixed during E2E testing

  • oclif v4 single-command CLI: commands config must use { strategy: "single", target: "..." } format instead of old string path + default key. Without this, flags like --debug were parsed as command names.
  • CNA 15 Turbopack prompt: create-next-app@15 now prompts interactively for Turbopack. Added --turbopack flag to prevent hanging.
  • LandingPageTemplate missing "use client": Template uses onLoad, localStorage, and window -- needs client directive for App Router.
  • Emotion jsxImportSource breaks App Router: Setting jsxImportSource: "@emotion/react" globally in tsconfig makes Server Components import Emotion's JSX runtime, causing createContext is not a function. Fix: only set for Pages Router, use per-file pragma for App Router.
  • Emotion cache.inserted type: EmotionCache.inserted is typed as Record<string, string | true | undefined> -- no any cast needed. Values of true indicate already-inserted styles that should be skipped.

CI fixes (PR #274)

  • Vitest 4 CLI changes: --json lists test cases not files (use --filesOnly), --poolOptions.* removed (use --no-fileParallelism)
  • ESM require.resolve(): Replace with import.meta.resolve() + fileURLToPath()
  • Node type stripping: Add verbatimModuleSyntax: true, convert 53 files to import type
  • Generated import extensions: Use extensionless imports (.ts/.tsx extensions fail across router configs)
  • Generated template lint errors: Fix no-explicit-any in LandingPage utils
  • pnpm frozen-lockfile in CI: Use explicit --no-frozen-lockfile flag (pnpm uses ci-info to detect CI)
  • Duplicate ThemeProvider: Alias MUI's to MuiThemeProvider when Chakra+MUI coexist
  • Plausible env var narrowing: Move null check from providerAfterImports to providerLogic for TS narrowing
  • pnpm @types/react corruption: auto-install-peers rewrites @types/react to ^6.0.0; fix by adding explicit @types/react@^19 devDep
  • Duplicate imports in generated code: Add prettier-plugin-organize-imports as tmpDependency to dedupe/sort imports during formatting
  • Lock file format check failure: Generate .prettierignore (with prettier plugin) to exclude lock files; E2E creates temp ignore when prettier not selected
  • Styled-components rules-of-hooks: Extract StyledComponentsRegistry component so early return doesn't make other plugins' hooks conditional

Phase 5: Website overhaul

  • Migrate from Chakra UI to Mantine v8
  • Migrate from Pages Router to App Router
  • Add Router selector to form
  • Update validation rules and command builder
  • Bump Next.js 13 -> 15, React 18 -> 19

Phase 6: Next.js 16 / CNA 16 support

  • Update create-next-app from v15 to v16, add --yes flag (skips React Compiler prompt)
  • ESLint: .eslintrc.json -> eslint.config.mjs (flat config, ESLint v9)
  • Lint script: next lint -> eslint
  • Prettier ESLint integration: update for flat config compat (eslint-config-prettier)
  • Update removeOfficialCNAContent for CNA 16 file structure
  • Fix Prettier formatting in CLI package (35 files reformatted)
  • Update E2E performFinalChecks for new lint command (already uses npm run lint)
  • Update website to Next.js 16

Phase 7: Update internal tooling dependencies

Website

  • ESLint ^9 -> ^10 Blocked: eslint-config-next@16 plugins don't support ESLint 10 yet
  • @types/node ^22 -> ^25

CLI internal tooling

  • ESLint ^8 -> ^10 + migrate .eslintrc to eslint.config.mjs (flat config)
  • @typescript-eslint/* ^7 -> ^8 (replaced with unified typescript-eslint package)
  • Jest ^29 -> ^30, @jest/globals ^29 -> ^30
  • next (devDep) ^15 -> ^16
  • @types/node ^20 -> ^25

Phase 8: Migrate CLI to ESM

  • tsconfig.json: module: "nodenext", moduleResolution: "nodenext", rewriteRelativeImportExtensions: true, drop declaration: true
  • package.json: add "type": "module"
  • bin/run + bin/dev: convert require() to import()
  • Replace __dirname usages with import.meta.dirname
  • Add .ts extensions to all relative imports
  • Bump chalk ^4 -> ^5 (ESM-only), execa ^5 -> ^9 (ESM-only)
  • Replace endent with aldent (ESM-native), published aldent 0.0.7 with compiled JS output
  • Replace Jest with Vitest (native ESM + .ts import support)
  • Verify build, lint, unit tests, and CLI execution

Phase 9: Post-merge review follow-ups (2026-04-18)

Discovered during a post-merge review of #274. PR did what it said, but two bugs slipped through and several "update to latest" todos are now stale after time passed.

Bugs

  • plugins/material-ui.ts:87: import path is @mui/material-nextjs/v15-appRouter but CNA is pinned to v16 — change to /v16-appRouter (breaks MUI + App Router on Next 16)
  • plugins/plausible.ts:11: next-plausible still pinned ^3.0.0; v4 shipped. Missed during Phase 2a "version checks".

Version currency (missed or released since merge)

  • TypeScript ^5 -> ^6
  • MUI ^7 -> ^9 (both @mui/material and @mui/material-nextjs; resolves the v15→v16 import path above)
  • Mantine ^8 -> ^9 (plugin + website)
  • vercel CLI ^50 -> ^51 (^50.0.0 does NOT cover v51)
  • netlify-cli ^24 -> ^25
  • GH Actions bump (both .github/workflows/*.yml AND plugins/github-actions.ts template):
    • actions/checkout@v4 -> @v6
    • actions/setup-node@v4 -> @v6
    • pnpm/action-setup@v4 -> @v5
  • Revisit Finish Prisma support #270: Prisma v7 is now released — re-evaluate the "too breaking" block. Still too breaking: v7 requires provider rename (prisma-client-jsprisma-client), mandatory output field, new prisma.config.ts, driver adapters (e.g. @prisma/adapter-better-sqlite3), import-path changes, removed middleware, disabled env auto-load. Needs a dedicated migration PR — left at ^6.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions