All skills
mblode avatar

/scaffold-nextjs

@a1355f3
by Matthew Blodemblode/agent-skills134 stars
12

Scaffolds a Next.js turborepo with Blode UI, icons, Ultracite (oxlint/shadcn), workspace hooks, and GitHub/Vercel setup. Use when asked to "create a Next.js project", "bootstrap a turborepo", or "start a new web app". For a page in an existing app use ui-design; for a CLI use scaffold-cli.

Use this Skill: https://skilld.dev/gh/mblode/agent-skills/scaffold-nextjs

This session only. Nothing lands on disk.

referencesturbo-configs.md

≈2.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Turborepo Config Templates

Contents


Root package.json

Create at {{name}}/package.json. Copy the ultracite version from apps/web/package.json so the two cannot drift, and set packageManager to pnpm@ plus the output of pnpm --version (corepack refuses a mismatch, and turbo needs the field to identify the package manager):

{
  "name": "{{name}}",
  "private": true,
  "packageManager": "pnpm@{{pnpm_version}}",
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev",
    "lint": "turbo lint",
    "lint:fix": "turbo lint:fix",
    "format": "turbo format",
    "format:check": "turbo format:check",
    "check-types": "turbo check-types",
    "check": "turbo lint format:check check-types",
    "fix": "turbo lint:fix format",
    "prepare": "lefthook install || true"
  },
  "devDependencies": {
    "lefthook": "^2",
    "turbo": "^2",
    "ultracite": "{{ultracite_version}}"
  }
}

check and fix go through turbo so they run inside apps/web, where the Oxlint and Oxfmt configs live. prepare installs the git hooks from the root lefthook.yml on every pnpm install; the || true keeps a CI or Vercel install from failing when there is no .git (Vercel builds have none).

pnpm-workspace.yaml

Create at {{name}}/pnpm-workspace.yaml. pnpm reads workspaces from this file and ignores a workspaces field in package.json:

packages:
  - "apps/*"

If create-next-app wrote apps/web/pnpm-workspace.yaml (recent versions do, to record which dependency build scripts such as sharp and unrs-resolver may run), copy its onlyBuiltDependencies / ignoredBuiltDependencies entries into this file and delete the app copy. pnpm only honours those settings at the workspace root, and a nested pnpm-workspace.yaml turns apps/web into a second root.

turbo.json

Create at {{name}}/turbo.json:

{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "out/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"]
    },
    "lint:fix": {
      "cache": false
    },
    "format": {
      "cache": false
    },
    "format:check": {
      "dependsOn": ["^build"]
    },
    "check-types": {
      "dependsOn": ["^build"]
    }
  }
}

When the build later needs a secret (an email API key, an analytics token), list it under build.passThroughEnv so turbo forwards it without hashing it into the cache key.

Root lefthook.yml

Create at {{name}}/lefthook.yml and delete apps/web/lefthook.yml. Lefthook loads the config next to .git; a file inside a workspace is read only when lefthook is invoked from that directory, which the git hook never does.

# `root` scopes each job to the workspace: lefthook filters staged files to
# that directory and passes them relative to it, which is what oxfmt and oxlint
# need because their config files live there rather than here.
pre-commit:
  parallel: true
  jobs:
    # oxfmt and oxlint directly, not `ultracite fix`: ultracite exits non-zero
    # when the staged set contains no lintable JS/TS file, so a docs-only or
    # CSS-only commit would fail the hook outright. Two jobs with their own
    # globs let lefthook skip whichever one has nothing to do.
    - name: oxfmt
      root: "apps/web/"
      # Matches what `format:check` inspects (`oxfmt --check .`), markdown
      # included. Omitting md/mdx lets a doc pass this hook and still fail the
      # format gate in CI.
      glob:
        - "*.js"
        - "*.jsx"
        - "*.ts"
        - "*.tsx"
        - "*.json"
        - "*.jsonc"
        - "*.css"
        - "*.md"
        - "*.mdx"
      run: pnpm exec oxfmt --write {staged_files}
      stage_fixed: true
    - name: oxlint
      root: "apps/web/"
      glob: "*.{js,jsx,ts,tsx}"
      run: pnpm exec oxlint --fix {staged_files}
      stage_fixed: true

Verify from the root after pnpm install:

pnpm exec lefthook run pre-commit --all-files

Root .gitignore

Create at {{name}}/.gitignore. apps/web/.gitignore from create-next-app stays in place and already covers .next/, .env*, and next-env.d.ts; this file covers the root and anything a second workspace adds later.

node_modules
out
dist
*.tgz

coverage
*.lcov

logs
*.log

.env
.env.development.local
.env.test.local
.env.production.local
.env.local

.eslintcache
.cache
*.tsbuildinfo

.next
next-env.d.ts

.idea
.DS_Store
.turbo
.vercel

# Agent scratch output stays out; knowledge files are the project's memory
# and must survive a machine change.
.claude/
!/.claude/
/.claude/*
!/.claude/knowledge/
!apps/web/.claude/
.cursor/
.vscode/

.env.local lives in apps/web/, where Next.js reads it, and apps/web/.gitignore already ignores .env*; vercel env pull apps/web/.env.local is the pull command.

knip.json

Create at {{name}}/knip.json:

{
  "$schema": "https://unpkg.com/knip@6/schema.json",
  "ignore": [".vercel/**"]
}

Run dead-code analysis on demand with pnpm dlx knip from the root (not a devDependency; pnpm dlx fetches it). Add workspace-specific entry points as needed (e.g. CLI apps or docs sites with custom entry files).

apps/web/package.json scripts

Replace the scripts block in apps/web/package.json. ultracite init left check and fix there; the root scripts replace them, so drop both. Keep the "type": "module" it added:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "oxlint .",
    "lint:fix": "oxlint --fix .",
    "format": "oxfmt --write .",
    "format:check": "oxfmt --check .",
    "check-types": "tsc --noEmit"
  }
}

Script names match the tasks in turbo.json so turbo can orchestrate them across workspaces. If you add a test runner later, add a matching test task to turbo.json at the same time; with node --test, glob the files (node --test "lib/**/*.test.ts") rather than naming one. Optional: portless <name> next dev as the dev script gives the app a stable https://<name>.localhost origin, so several apps run side by side without port juggling.

Tailwind's oxide and lightningcss ship platform-specific binaries. pnpm-lock.yaml records every platform's optional package, so a lockfile generated on macOS still installs the Linux binaries on Vercel; do not pin @tailwindcss/oxide-linux-x64-gnu or lightningcss-linux-x64-gnu in optionalDependencies (that workaround is for npm lockfiles). Do set an explicit browserslist so CSS output does not change when the default query moves.

apps/web/next.config.ts

Verify apps/web/next.config.ts still has React Compiler and the Instant Navigations flags after the move:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  cacheComponents: true,
  partialPrefetching: true,
  reactCompiler: true,
  deploymentId: process.env.VERCEL_DEPLOYMENT_ID,
  poweredByHeader: false,
  experimental: {
    // blode-icons-react is not on Next's built-in optimizePackageImports list
    // (lucide-react is), so name it or every icon import pulls the barrel.
    optimizePackageImports: ["blode-icons-react"],
    turbopackRustReactCompiler: true,
  },
};

export default nextConfig;

create-next-app generates this file when React Compiler is selected; verify reactCompiler: true is present. The others come from Phase 2.2 and must survive the move into apps/web/, since dropping cacheComponents silently takes partialPrefetching with it. No turbopack.root is needed: Turbopack infers the workspace root from the lockfile at {{name}}/pnpm-lock.yaml. If the config ever imports a project module (a basePath constant, a site URL), import it by relative path: Next compiles next.config.ts without tsconfig path resolution, so an @/ alias resolves against the wrong directory. experimental.useOffline: true is worth turning on once the app has forms; it holds a navigation or Server Action through a connectivity drop and retries on reconnect instead of throwing.

Root AGENTS.md

Ultracite's AGENTS.md and the Next-managed block both live in apps/web/. The root needs its own short file so an agent opening the repo runs commands from the right directory. Create {{name}}/AGENTS.md:

# {{name}}

Turborepo. The site lives in `apps/web` (see that app's `AGENTS.md` for code
standards and the Next.js docs pointer). Everything here applies to every task,
including ones that only touch root config.

## Commands

Run these from this directory, not from `apps/web`; they go through Turborepo:

```bash
pnpm run dev           # start the site
pnpm run build         # build every workspace
pnpm run lint          # oxlint, including ultracite/oxlint/shadcn
pnpm run lint:fix      # oxlint --fix
pnpm run format        # oxfmt --write .   (scope to your changes)
pnpm run format:check  # oxfmt --check
pnpm run check-types   # tsc --noEmit
pnpm run check         # lint + format:check + check-types
pnpm run fix           # lint:fix + format
```

pnpm is the package manager (`pnpm-lock.yaml`, `pnpm-workspace.yaml`); do not
run npm or yarn installs. Add an app dependency with
`pnpm --filter web add <pkg>`, never at the root.

`pnpm run check` is the combined quality gate; the pre-commit hook runs the same
tools on staged files. `pnpm run lint` must run inside `apps/web` (turbo does
that) so Oxlint loads `oxlint.config.ts` and `ultracite/oxlint/shadcn`.

## Rules

- No em dashes in copy, content, docs, or commit messages. Rephrase with a
  colon, comma, or a separate sentence.
- `next.config.ts` keeps `cacheComponents`, `partialPrefetching`, and
  `reactCompiler` on. Read the Instant Navigations rules in
  `apps/web/AGENTS.md` before adding a route.
- Ultracite's `ultracite/oxlint/shadcn` preset (`@shadcn/lint`) enforces
  design-system `className` contracts: call sites may add layout classes
  (`mt-4`, `w-full`); restyle in `components/ui/` instead. After UI work,
  `pnpm exec ultracite fix` then `pnpm exec ultracite fix --codex` from
  `apps/web`.

Use AGENTS.md directly without a CLAUDE.md wrapper. After the first pnpm run dev from the coding agent's shell, confirm apps/web/AGENTS.md ends with the <!-- BEGIN:nextjs-agent-rules --> block and commit it; remove any generated duplicate apps/web/CLAUDE.md. The Phase 5.1 design-system lint paragraph stays above those markers.

Source: SKILL.md on GitHub

1 warningtoday5 checks · Risk SAFE
  • Gen Agent Trust Hubtoday

    A project scaffolding skill for Next.js and Blode UI. It performs standard development tasks like installing dependencies and deploying to Vercel. A minor security risk exists due to the interpolation of user-provided project names and repository paths into shell commands without explicit sanitization.

  • Sockettoday

    No alerts

  • Snyktoday

    Risk: LOW · No issues

  • Runlayer6mo

    2/4 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at a1355f3. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 5 days ago
compatibility
Requires a shell, Git, Node.js, npm, and package registry access.

README badge

README badge for mblode/agent-skills/scaffold-nextjs