---
title: "Create Next App"
description: "create-next-app serves as the official scaffolding utility for initializing Next.js applications, automating the end-to-end setup process from command-line parsing to a ready-to-run repository pack..."
last_updated: "2026-10-12T06:38:56.853737+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/companion-packages/create-next-app"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/create-next-app/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts)
- [packages/create-next-app/create-app.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts)
- [packages/create-next-app/templates/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts)
- [packages/next/src/cli/next-upgrade.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/cli/next-upgrade.ts)
- [packages/next/src/bin/next.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/bin/next.ts)
- [packages/next-codemod/bin/upgrade.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/bin/upgrade.ts)
- [packages/next/src/cli/next-info.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/cli/next-info.ts)
- [packages/next/src/cli/next-dev.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/cli/next-dev.ts)
- [packages/create-next-app/helpers/install.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/install.ts)
- [packages/create-next-app/helpers/examples.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts)
- [packages/next-codemod/bin/next-codemod.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/bin/next-codemod.ts)
- [packages/next-codemod/bin/agents-md.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/bin/agents-md.ts)
- [packages/create-next-app/package.json](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/package.json)
- [packages/next/src/lib/helpers/install.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next/src/lib/helpers/install.ts)
- [packages/create-next-app/helpers/is-folder-empty.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/is-folder-empty.ts)
- [packages/next-codemod/lib/install.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/next-codemod/lib/install.ts)
- [packages/create-next-app/helpers/git.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/git.ts)
- [packages/create-next-app/helpers/validate-pkg.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/validate-pkg.ts)
- [packages/create-next-app/helpers/is-writeable.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/is-writeable.ts)
- [packages/create-next-app/helpers/get-pkg-manager.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/get-pkg-manager.ts)
</details>

## Overview

`create-next-app` serves as the official scaffolding utility for initializing Next.js applications, automating the end-to-end setup process from command-line parsing to a ready-to-run repository [packages/create-next-app/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts), [packages/create-next-app/create-app.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts). By combining interactive prompts with declarative CLI flags via `commander` and `prompts`, it resolves project configurations for TypeScript or JavaScript, Tailwind CSS, App or Pages routers, and alternative bundlers like Turbopack and Rspack [packages/create-next-app/index.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts). The orchestrator validates target directories, pulls template structures or remote GitHub examples, executes package installations across `npm`, `pnpm`, `yarn`, or `bun`, and finalizes the workspace with optional Git repository initialization [packages/create-next-app/create-app.ts](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts). Sources: [packages/create-next-app/index.ts:4-10](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L4-L10), [packages/create-next-app/create-app.ts:28-70](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts#L28-L70)

## CLI Entry Point and Interactive Prompts

### Argument Parsing and Command Configuration

The entry point script `packages/create-next-app/index.ts` executes as a Node.js CLI tool via the shebang `#!/usr/bin/env node`. It initializes command-line parsing using `commander` configured with the package name and version from `packages/create-next-app/package.json`. Signal handlers for `SIGINT` and `SIGTERM` invoke `process.exit(0)`, while a prompt state handler monitors prompt cancellation to restore the terminal cursor using the ANSI escape sequence `\x1B[?25h` before terminating with `process.exit(1)`.

```typescript
#!/usr/bin/env node
/* eslint-disable import/no-extraneous-dependencies */
import ciInfo from 'ci-info'
import { Command } from 'commander'
import Conf from 'conf'
import { existsSync } from 'node:fs'
import { basename, resolve } from 'node:path'
import { blue, bold, cyan, green, red, yellow } from 'picocolors'
import type { InitialReturnValue } from 'prompts'
import prompts from 'prompts'
import updateCheck from 'update-check'
import { createApp, DownloadError } from './create-app'
import type { PackageManager } from './helpers/get-pkg-manager'
import { getPkgManager } from './helpers/get-pkg-manager'
import { isFolderEmpty } from './helpers/is-folder-empty'
import { validateNpmName } from './helpers/validate-pkg'
import packageJson from './package.json'
import { Bundler } from './templates'

let projectPath: string = ''

const handleSigTerm = () => process.exit(0)

process.on('SIGINT', handleSigTerm)
process.on('SIGTERM', handleSigTerm)

const onPromptState = (state: {
  value: InitialReturnValue
  aborted: boolean
  exited: boolean
}) => {
  if (state.aborted) {
    // If we don't re-enable the terminal cursor before exiting
    // the program, the cursor will remain hidden
    process.stdout.write('\x1B[?25h')
    process.stdout.write('\n')
    process.exit(1)
  }
}
```
Sources: [packages/create-next-app/index.ts:1-39](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L1-L39)

The CLI defines a positional argument `[directory]` and accepts numerous boolean flags and options to configure scaffolding preferences without interactive prompts. 

| Option Flag | Description |
| :--- | :--- |
| `-v, --version` | Output the current version of create-next-app |
| `-h, --help` | Display this help message |
| `--ts, --typescript` | Initialize as a TypeScript project (default) |
| `--js, --javascript` | Initialize as a JavaScript project |
| `--tailwind` | Initialize with Tailwind CSS config (default) |
| `--react-compiler` | Initialize with React Compiler enabled |
| `--cache-components` | Initialize with Cache Components enabled |
| `--eslint` | Initialize with ESLint config |
| `--biome` | Initialize with Biome config |
| `--app` | Initialize as an [App Router](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/client-runtime/app-router) project |
| `--src-dir` | Initialize inside a `src/` directory |
| `--rspack` | Enable Rspack as the bundler |
| `--import-alias <prefix/*>` | Specify import alias to use (default `@/*`) |
| `--api` | Initialize a headless API using the App Router |
| `--empty` | Initialize an empty project |
| `--use-npm` | Bootstrap using npm |
| `--use-pnpm` | Bootstrap using pnpm |
| `--use-yarn` | Bootstrap using Yarn |
| `--use-bun` | Bootstrap using Bun |
| `--reset, --reset-preferences` | Reset saved preferences |
| `--skip-install` | Skip installing packages |
| `--yes` | Use saved preferences or defaults for unprovided options |
| `-e, --example <name\|url>` | Bootstrap with an example name or public GitHub URL |
| `--example-path <path>` | Specify path to example for complex URLs |
| `--agents-md` | Include AGENTS.md to guide coding agents (default) |
| `--agent-feedback` | Prepare anonymized Next.js feedback |
| `--no-agent-feedback` | Do not prepare anonymized Next.js feedback |
| `--disable-git` | Skip initializing a git repository |

```typescript
const program = new Command(packageJson.name)
  .version(
    packageJson.version,
    '-v, --version',
    'Output the current version of create-next-app.'
  )
  .argument('[directory]')
  .usage('[directory] [options]')
  .helpOption('-h, --help', 'Display this help message.')
  .option('--ts, --typescript', 'Initialize as a TypeScript project. (default)')
  .option('--js, --javascript', 'Initialize as a JavaScript project.')
  .option('--tailwind', 'Initialize with Tailwind CSS config. (default)')
  .option('--react-compiler', 'Initialize with React Compiler enabled.')
  .option('--cache-components', 'Initialize with Cache Components enabled.')
  .option('--eslint', 'Initialize with ESLint config.')
  .option('--biome', 'Initialize with Biome config.')
  .option('--app', 'Initialize as an App Router project.')
  .option('--src-dir', "Initialize inside a 'src/' directory.")
  .option('--rspack', 'Enable Rspack as the bundler.')
  .option(
    '--import-alias <prefix/*>',
    'Specify import alias to use (default "@/*").'
  )
  .option('--api', 'Initialize a headless API using the App Router.')
  .option('--empty', 'Initialize an empty project.')
  .option(
    '--use-npm',
    'Explicitly tell the CLI to bootstrap the application using npm.'
  )
  .option(
    '--use-pnpm',
    'Explicitly tell the CLI to bootstrap the application using pnpm.'
  )
  .option(
    '--use-yarn',
    'Explicitly tell the CLI to bootstrap the application using Yarn.'
  )
  .option(
    '--use-bun',
    'Explicitly tell the CLI to bootstrap the application using Bun.'
  )
  .option(
    '--reset, --reset-preferences',
    'Reset the preferences saved for create-next-app.'
  )
  .option(
    '--skip-install',
    'Explicitly tell the CLI to skip installing packages.'
  )
  .option('--yes', 'Use saved preferences or defaults for unprovided options.')
  .option(
    '-e, --example <example-name|github-url>',
    `

  An example to bootstrap the app with. You can use an example name
  from the official Next.js repo or a public GitHub URL. The URL can use
  any branch and/or subdirectory.
`
  )
  .option(
    '--example-path <path-to-example>',
    `

  In a rare case, your GitHub URL might contain a branch name with
  a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar).
  In this case, you must specify the path to the example separately:
  --example-path foo/bar
`
  )
  .option(
    '--agents-md',
    'Include AGENTS.md to guide coding agents to write up-to-date Next.js code. (default)'
  )
  .option(
    '--agent-feedback',
    'Prepare anonymized Next.js feedback for your review.'
  )
  .option(
    '--no-agent-feedback',
    'Do not prepare anonymized Next.js feedback for your review.'
```
Sources: [packages/create-next-app/index.ts:41-133](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L41-L133)

> [!NOTE]
> Commander does not natively support negated flags like `--no-agent-feedback`. The action handler explicitly checks if positional arguments start with `--no-` to correctly capture user intent without treating negated options as project directory names.

```typescript
.action((name) => {
  // Commander does not implicitly support negated options. When they are used
  // by the user they will be interpreted as the positional argument (name) in
  // the action handler. See https://github.com/tj/commander.js/pull/1355
  if (name && !name.startsWith('--no-')) {
    projectPath = name
  }
})
```
Sources: [packages/create-next-app/index.ts:123-130](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L123-L130)

### Package Manager Resolution and Preference Management

The package manager is determined by inspecting explicit CLI flags (`--use-npm`, `--use-pnpm`, `--use-yarn`, or `--use-bun`) in order. If none of these flags are provided, the CLI falls back to detecting the invoking package manager via `getPkgManager()`.

```typescript
const packageManager: PackageManager = !!opts.useNpm
  ? 'npm'
  : !!opts.usePnpm
    ? 'pnpm'
    : !!opts.useYarn
      ? 'yarn'
      : !!opts.useBun
        ? 'bun'
        : getPkgManager()

```
Sources: [packages/create-next-app/index.ts:137-146](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L137-L146)

The `run` asynchronous function initializes a `Conf` instance with `projectName: 'create-next-app'` to persist user preferences across executions. If `--reset` or `--reset-preferences` is passed, an interactive toggle prompt asks the user whether to clear stored configurations, calling `conf.clear()` upon confirmation.

```typescript
async function run(): Promise<void> {
  const conf = new Conf({ projectName: 'create-next-app' })

  if (opts.resetPreferences) {
    const { resetPreferences } = await prompts({
      onState: onPromptState,
      type: 'toggle',
      name: 'resetPreferences',
      message: 'Would you like to reset the saved preferences?',
      initial: false,
      active: 'Yes',
      inactive: 'No',
    })
    if (resetPreferences) {
      conf.clear()
      console.log('The preferences have been reset successfully!')
    }
    process.exit(0)
  }
```
Sources: [packages/create-next-app/index.ts:147-165](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L147-L165)

If no project path is provided via command-line arguments, `prompts` requests a project name interactively, validating input directory names using `validateNpmName` from `packages/create-next-app/helpers/validate-pkg.ts` alongside `validate-npm-package-name`.

```typescript
if (typeof projectPath === 'string') {
  projectPath = projectPath.trim()
}

if (!projectPath) {
  const res = await prompts({
    onState: onPromptState,
    type: 'text',
    name: 'path',
    message: 'What is your project named?',
    initial: 'my-app',
    validate: (name) => {
      const validation = validateNpmName(basename(resolve(name)))
      if (validation.valid) {
        return true
      }
      return 'Invalid project name: ' + validation.problems[0]
    },
  })

  if (typeof res.path === 'string') {
    projectPath = res.path.trim()
  }
}
```
Sources: [packages/create-next-app/index.ts:167-190](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L167-L190)

```typescript
export function validateNpmName(name: string): ValidateNpmNameResult {
  const nameValidation = validateProjectName(name)
  if (nameValidation.validForNewPackages) {
    return { valid: true }
  }

  return {
    valid: false,
    problems: [
      ...(nameValidation.errors || []),
      ...(nameValidation.warnings || []),
    ],
  }
}
```
Sources: [packages/create-next-app/helpers/validate-pkg.ts:13-26](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/validate-pkg.ts#L13-L26)

## Directory Validation and Preflight Checks

### Naming Verification and Destination Resolution

Before scaffolding begins, `create-next-app` evaluates the target project path and package identity to verify compatibility with npm naming conventions. The application extracts the base directory name from the resolved path and invokes `validateNpmName`, which delegates package name evaluation to `validate-npm-package-name`. If `validForNewPackages` is false, the CLI gathers all validation errors and warnings into a problem array, prints detailed error diagnostics, and exits with status code 1.

```typescript
export function validateNpmName(name: string): ValidateNpmNameResult {
  const nameValidation = validateProjectName(name)
  if (nameValidation.validForNewPackages) {
    return { valid: true }
  }

  return {
    valid: false,
    problems: [
      ...(nameValidation.errors || []),
      ...(nameValidation.warnings || []),
    ],
  }
}
```
Sources: [packages/create-next-app/helpers/validate-pkg.ts:13-26](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/validate-pkg.ts#L13-L26)

> [!WARNING]
> If a user passes an empty project path argument or omits it entirely without answering the interactive prompt, the CLI terminates immediately after displaying usage instructions via `opts.name()` rather than proceeding with an undefined or current working directory target.

```typescript
if (!projectPath) {
  console.log(
    '\nPlease specify the project directory:\n' +
      `  ${cyan(opts.name())} ${green('<project-directory>')}\n` +
      'For example:\n' +
      `  ${cyan(opts.name())} ${green('my-next-app')}\n\n` +
      `Run ${cyan(`${opts.name()} --help`)} to see all options.`
  )
  process.exit(1)
}
```
Sources: [packages/create-next-app/index.ts:192-201](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/index.ts#L192-L201)

### Write Permissions and Folder Emptiness Checks

Once the project path is resolved, `createApp` validates that the parent directory of the target root is writable by testing `W_OK` access via `node:fs/promises`. If the parent directory lacks write permissions, an error message is written to `stderr` and the process exits.

```typescript
export async function isWriteable(directory: string): Promise<boolean> {
  try {
    await access(directory, W_OK)
    return true
  } catch (err) {
    return false
  }
}
```
Sources: [packages/create-next-app/helpers/is-writeable.ts:4-11](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/is-writeable.ts#L4-L11)

The CLI recursively creates the destination directory using `mkdirSync(root, { recursive: true })` and inspects its contents with `isFolderEmpty`. This helper function reads the directory contents and filters out standard non-conflicting configuration artifacts, temporary files, and editor directories.

| File / Folder Pattern | Status | Purpose / Rationale |
| :--- | :--- | :--- |
| `.claude`, `.cursor`, `.vscode`, `.zed` | Whitelisted | AI coding assistant and editor configuration directories |
| `.git`, `.hg`, `.hgcheck`, `.hgignore` | Whitelisted | Version control directories and ignore rules |
| `.DS_Store`, `Thumbs.bg`, `Thumbs.db` | Whitelisted | Operating system thumbnail and metadata files |
| `.gitignore`, `.gitattributes`, `.npmignore` | Whitelisted | Git and npm tracking exclusion rules |
| `LICENSE`, `docs`, `mkdocs.yml` | Whitelisted | Documentation and licensing assets |
| `npm-debug.log`, `yarn-debug.log`, `yarn-error.log` | Whitelisted | Package manager diagnostic and error logs |
| `yarnrc.yml`, `.yarn` | Whitelisted | Yarn workspace configuration and cache folders |
| `*.iml` | Whitelisted (Regex) | IntelliJ IDEA project module files |

Sources: [packages/create-next-app/helpers/is-folder-empty.ts:7-39](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/is-folder-empty.ts#L7-L39)

> [!CAUTION]
> If any conflicting files or subdirectories not present on the whitelist are found inside the target directory, `isFolderEmpty` prints each conflicting path (appending a trailing slash to directories) and returns `false`, causing `createApp` to terminate the process before any template files are written.

```typescript
if (conflicts.length > 0) {
  console.log(
    `The directory ${green(name)} contains files that could conflict:`
  )
  console.log()
  for (const file of conflicts) {
    try {
      const stats = lstatSync(join(root, file))
      if (stats.isDirectory()) {
        console.log(`  ${blue(file)}/`)
      } else {
        console.log(`  ${file}`)
      }
    } catch {
      console.log(`  ${file}`)
    }
  }
  console.log()
  console.log(
    'Either try using a new directory name, or remove the files listed above.'
  )
  console.log()
  return false
}
```
Sources: [packages/create-next-app/helpers/is-folder-empty.ts:41-64](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/is-folder-empty.ts#L41-L64)

## Template Composition and File Generation

### Template Selection and Scaffold Copying

The generator determines the initial template variant by combining the application routing structure, styling selection, and emptiness flags into a `TemplateType` string. The template mode (`ts` or `js`) is established based on the TypeScript boolean flag, which directs path resolution to the appropriate subdirectory within `packages/create-next-app/templates/`.

```typescript
  const mode: TemplateMode = typescript ? 'ts' : 'js'
  const template: TemplateType = `${app ? 'app' : 'default'}${tailwind ? '-tw' : ''}${empty ? '-empty' : ''}`
```
Sources: [packages/create-next-app/create-app.ts:72-73](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts#L72-L73)

When installing a template, `installTemplate` builds an exclusion array for the copy operation based on enabled features and bundler configuration. Specifically, configuration files such as `eslint.config.mjs`, `biome.json`, and `postcss.config.mjs` are excluded when their respective flags or bundlers are omitted or set to Turbopack.

| Excluded File Pattern | Condition for Exclusion |
| :--- | :--- |
| `!eslint.config.mjs` | `!eslint` (ESLint disabled) |
| `!biome.json` | `!biome` (Biome disabled) |
| `!postcss.config.mjs` | `!tailwind || bundler === Bundler.Turbopack` |

Sources: [packages/create-next-app/templates/index.ts:74-79](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L74-L79)

> [!NOTE]
> During file copying, the `rename` transform intercepts specific filenames to normalize them for project creation. Notably, `gitignore` is renamed to `.gitignore` to prevent npm publish omissions, and `README-template.md` is output as `README.md`.

```typescript
rename(name) {
  switch (name) {
    case "gitignore": {
      return `.${name}`;
    }
    // README.md is ignored by webpack-asset-relocator-loader used by ncc:
    // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227
    case "README-template.md": {
      return "README.md";
    }
    default: {
      return name;
    }
  }
},
```
Sources: [packages/create-next-app/templates/index.ts:84-98](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L84-L98)

### Bundler Integration and Config Patching

After copying template files, the generator updates configuration files to support alternative bundlers, experimental features, and custom import paths. For Rspack, `next-rspack` integration is injected into the Next.js configuration file by wrapping the exported configuration.

```typescript
if (bundler === Bundler.Rspack) {
  const nextConfigFile = path.join(
    root,
    mode === "js" ? "next.config.mjs" : "next.config.ts",
  );
  await fs.writeFile(
    nextConfigFile,
    `import withRspack from "next-rspack";\n\n` +
      (await fs.readFile(nextConfigFile, "utf8")).replace(
        "export default nextConfig;",
        "export default withRspack(nextConfig);",
      ),
  );
}
```
Sources: [packages/create-next-app/templates/index.ts:101-114](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L101-L114)

Config mutations are also applied when feature flags are enabled during template instantiation. The following options modify `next.config.mjs` or `next.config.ts` by searching for the `/* config options here */` placeholder.

| Feature Flag | Configuration Property Injected |
| :--- | :--- |
| `tailwind` (with Turbopack) | `turbopack: { rules: { "*.css": { loaders: ["@tailwindcss/turbopack"], as: "*.css" } } }` |
| `reactCompiler` | `reactCompiler: true` |
| `cacheComponents` | `cacheComponents: true`, `partialPrefetching: true` |
| `agentFeedback` | `experimental: { agentFeedback: true }` |

Sources: [packages/create-next-app/templates/index.ts:116-186](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L116-L186)

> [!WARNING]
> When `cacheComponents` is enabled, `partialPrefetching` is automatically enabled alongside it in the generated Next.js configuration because the framework design couples these two options.

```typescript
if (cacheComponents) {
  const nextConfigFile = path.join(
    root,
    mode === "js" ? "next.config.mjs" : "next.config.ts",
  );
  let configContent = await fs.readFile(nextConfigFile, "utf8");

  // `partialPrefetching` is enabled alongside `cacheComponents` because the
  // only reason to disable it is when adopting `cacheComponents` from before
  // the option existed.
  configContent = configContent.replace(
    "/* config options here */\n",
    "/* config options here */\n  cacheComponents: true,\n  partialPrefetching: true,\n",
  );

  await fs.writeFile(nextConfigFile, configContent);
}
```
Sources: [packages/create-next-app/templates/index.ts:155-171](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L155-L171)

### Directory Structure and Import Aliases

When the `srcDir` option is active, the installer creates a `src/` directory and relocates standard folders into it. The source directory migration moves specific directories defined in `SRC_DIR_NAMES` and updates default page template imports.

```typescript
export const SRC_DIR_NAMES = ["app", "pages", "styles"];
```
Sources: [packages/create-next-app/templates/index.ts:43-43](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L43-L43)

```typescript
if (srcDir) {
  await fs.mkdir(path.join(root, "src"), { recursive: true });
  await Promise.all(
    SRC_DIR_NAMES.map(async (file) => {
      await fs
        .rename(path.join(root, file), path.join(root, "src", file))
        .catch((err) => {
          if (err.code !== "ENOENT") {
            throw err;
          }
        });
    }),
  );

```
Sources: [packages/create-next-app/templates/index.ts:240-253](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L240-L253)

Import aliases are configured in `tsconfig.json` or `jsconfig.json` by replacing default paths with the specified `importAlias` string. If a custom alias other than `@/*` is chosen, a concurrent file walker using an `async-sema` concurrency pool of size 8 scans project files while ignoring version control folders, font assets, and lockfiles to rewrite module imports.

```typescript
if (importAlias !== "@/*") {
  const files = await glob("**/*", {
    cwd: root,
    dot: true,
    stats: false,
    // We don't want to modify compiler options in [ts/js]config.json
    // and none of the files in the .git folder
    // TODO: Refactor this to be an allowlist, rather than a denylist,
    // to avoid corrupting files that weren't intended to be replaced

    ignore: [
      "tsconfig.json",
      "jsconfig.json",
      ".git/**/*",
      "**/fonts/**",
      "**/favicon.ico",
    ],
  });
  const writeSema = new Sema(8, { capacity: files.length });
  await Promise.all(
    files.map(async (file) => {
      await writeSema.acquire();
      const filePath = path.join(root, file);
      if ((await fs.stat(filePath)).isFile()) {
        await fs.writeFile(
          filePath,
          (await fs.readFile(filePath, "utf8")).replace(
            `@/`,
            `${importAlias.replace(/\*/g, "")}`,
          ),
        );
      }
      writeSema.release();
    }),
  );
}
```
Sources: [packages/create-next-app/templates/index.ts:203-238](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/templates/index.ts#L203-L238)

## Remote Example Extraction and Fallbacks

### URL Parsing and Repository Validation

When an example argument is supplied to `createApp`, the input is first evaluated to determine if it represents a full GitHub repository URL or a built-in template name. If `example` can be successfully parsed as a `URL` object originating from `https://github.com`, the helper `getRepoInfo` breaks down the pathname into structural components.

```typescript
export async function getRepoInfo(
  url: URL,
  examplePath?: string
): Promise<RepoInfo | undefined> {
  const [, username, name, t, _branch, ...file] = url.pathname.split('/')
  const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/')

  if (
    // Support repos whose entire purpose is to be a Next.js example, e.g.
    // https://github.com/:username/:my-cool-nextjs-example-repo-name.
    t === undefined ||
    // Support GitHub URL that ends with a trailing slash, e.g.
    // https://github.com/:username/:my-cool-nextjs-example-repo-name/
    // In this case "t" will be an empty string while the next part "_branch" will be undefined
    (t === '' && _branch === undefined)
  ) {
    try {
      const infoResponse = await fetch(
        `https://api.github.com/repos/${username}/${name}`
      )
      if (infoResponse.status !== 200) {
        return
      }

      const info = await infoResponse.json()
      return { username, name, branch: info['default_branch'], filePath }
    } catch {
      return
    }
  }

  // If examplePath is available, the branch name takes the entire path
  const branch = examplePath
    ? `${_branch}/${file.join('/')}`.replace(new RegExp(`/${filePath}|/$`), '')
    : _branch

  if (username && name && branch && t === 'tree') {
    return { username, name, branch, filePath }
  }
}
```
Sources: [packages/create-next-app/helpers/examples.ts:23-62](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L23-L62)

The `RepoInfo` data structure captures the parsed coordinates required for remote tarball extraction:

```typescript
export type RepoInfo = {
  username: string
  name: string
  branch: string
  filePath: string
}
```
Sources: [packages/create-next-app/helpers/examples.ts:7-12](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts:7-12)

> [!WARNING]
> Only repositories hosted on `https://github.com` are permitted; passing non-GitHub URLs triggers an immediate termination with process exit code `1`.

```typescript
if (repoUrl) {
  if (repoUrl.origin !== 'https://github.com') {
    console.error(
      `Invalid URL: ${red(
        `"${example}"`
      )}. Only GitHub repositories are supported. Please use a GitHub URL and try again.`
    )
    process.exit(1)
  }

```
Sources: [packages/create-next-app/create-app.ts:89-98](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts#L89-L98)

Before any downloading commences, `hasRepo` checks whether a `package.json` file can be reached via the raw GitHub user content network to verify that the target directory and branch exist.

```typescript
export function hasRepo({
  username,
  name,
  branch,
  filePath,
}: RepoInfo): Promise<boolean> {
  // Avoid GitHub's low unauthenticated API rate limit. create-next-app only
  // needs to verify that the example's package.json can be downloaded.
  const packagePath = `${filePath ? `${filePath}/` : ''}package.json`

  return isUrlOk(
    `https://raw.githubusercontent.com/${username}/${name}/${branch}/${packagePath}`
  )
}
```
Sources: [packages/create-next-app/helpers/examples.ts:64-77](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L64-L77)

If the `example` string is not a valid URL, it is evaluated as a built-in example residing within the main `vercel/next.js` repository by querying the GitHub contents API or verifying an explicit URL status via `isUrlOk`.

```typescript
export function existsInRepo(nameOrUrl: string): Promise<boolean> {
  try {
    const url = new URL(nameOrUrl)
    return isUrlOk(url.href)
  } catch {
    return isUrlOk(
      `https://api.github.com/repos/vercel/next.js/contents/examples/${encodeURIComponent(
        nameOrUrl
      )}`
    )
  }
}
```
Sources: [packages/create-next-app/helpers/examples.ts:79-90](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L79-L90)

### Tarball Streaming and Extraction Filters

Remote code extraction relies on streaming tarballs through `downloadTarStream` combined with the `tar` library's `x` extractor. Depending on whether a custom repository or a built-in framework example was requested, the extraction process invokes either `downloadAndExtractRepo` or `downloadAndExtractExample`.

```typescript
async function downloadTarStream(url: string) {
  const res = await fetch(url)

  if (!res.body) {
    throw new Error(`Failed to download: ${url}`)
  }

  return Readable.fromWeb(res.body as import('stream/web').ReadableStream)
}
```
Sources: [packages/create-next-app/helpers/examples.ts:92-100](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L92-L100)

```typescript
export async function downloadAndExtractRepo(
  root: string,
  { username, name, branch, filePath }: RepoInfo
) {
  let rootPath: string | null = null
  await pipeline(
    await downloadTarStream(
      `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`
    ),
    x({
      cwd: root,
      strip: filePath ? filePath.split('/').length + 1 : 1,
      filter: (p: string) => {
        // Convert Windows path separators to POSIX style
        const posixPath = p.split(sep).join(posix.sep)

        // Determine the unpacked root path dynamically instead of hardcoding to the fetched repo's name / branch.
        // This avoids the condition when the repository has been renamed, and the old repository name is used to fetch the example.
        // The tar download will work as it is redirected automatically, but the root directory of the extracted
        // example will be the new, renamed name instead of the name used to fetch the example, breaking the filter.
        if (rootPath === null) {
          const pathSegments = posixPath.split(posix.sep)
          rootPath = pathSegments.length ? pathSegments[0] : null
        }

        return posixPath.startsWith(
          `${rootPath}${filePath ? `/${filePath}/` : '/'}`
        )
      },
    })
  )
}
```
Sources: [packages/create-next-app/helpers/examples.ts:102-133](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L102-L133)

> [!NOTE]
> During custom repository extraction, the filter dynamically resolves the unpacked root directory from the incoming POSIX path segments rather than hardcoding the repository name or branch. This prevents path-matching failures if a GitHub repository was recently renamed while redirects were active.

```typescript
export async function downloadAndExtractExample(root: string, name: string) {
  if (name === '__internal-testing-retry') {
    throw new Error('This is an internal example for testing the CLI.')
  }

  await pipeline(
    await downloadTarStream(
      'https://codeload.github.com/vercel/next.js/tar.gz/canary'
    ),
    x({
      cwd: root,
      strip: 2 + name.split('/').length,
      filter: (p) => p.includes(`next.js-canary/examples/${name}/`),
    })
  )
}
```
Sources: [packages/create-next-app/helpers/examples.ts:135-150](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/examples.ts#L135-L150)

## Package Manager Detection and Installation

### Package Manager Resolution

The tool resolves the active package manager by inspecting the `npm_config_user_agent` environment variable. If this string is not set or does not match a known prefix, it defaults to `npm`.

| Package Manager | Environment Prefix / Command | Version Resolution Fallback |
| :--- | :--- | :--- |
| `yarn` | `yarn` | `yarn --version` |
| `pnpm` | `pnpm` | `pnpm --version` |
| `bun` | `bun` | `bun --version` |
| `npm` | (default fallback) | `npm --version` |

```typescript
export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'

export function getPkgManager(): PackageManager {
  const userAgent = process.env.npm_config_user_agent || ''

  if (userAgent.startsWith('yarn')) {
    return 'yarn'
  }

  if (userAgent.startsWith('pnpm')) {
    return 'pnpm'
  }

  if (userAgent.startsWith('bun')) {
    return 'bun'
  }

  return 'npm'
}
```
Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:3-21](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/get-pkg-manager.ts#L3-L21)

To acquire precise version metadata, `getPackageManagerVersion` searches `npm_config_user_agent` with a dynamic RegExp matching `${packageManager}/([\d.]+[\w-]*)`. If unpopulated, it executes `<packageManager> --version` via `execSync` with standard output suppressed on error.

```typescript
export function getPackageManagerVersion(
  packageManager: PackageManager
): string | null {
  const userAgent = process.env.npm_config_user_agent || ''
  const userAgentMatch = userAgent.match(
    new RegExp(`${packageManager}/([\\d.]+[\\w.-]*)`)
  )
  if (userAgentMatch) {
    return userAgentMatch[1]
  }

  try {
    const version = execSync(`${packageManager} --version`, {
      encoding: 'utf8',
      stdio: ['pipe', 'pipe', 'ignore'],
    }).trim()
    if (/^\d+\.\d+\.\d+/.test(version)) {
      return version
    }
  } catch {
    // package manager not available or failed to run
  }

  return null
}
```
Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:30-54](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/get-pkg-manager.ts#L30-L54)

> [!NOTE]
> When evaluating `pnpm`, `getPnpmMajorVersion` queries `getPackageManagerVersion('pnpm')`, splits the resulting version string by dots, and parses the primary integer index to gate major-version-specific scaffolding behaviors.

```typescript
export function getPnpmMajorVersion(): number | null {
  const version = getPackageManagerVersion('pnpm')
  if (!version) return null
  const major = parseInt(version.split('.')[0], 10)
  return Number.isNaN(major) ? null : major
}
```
Sources: [packages/create-next-app/helpers/get-pkg-manager.ts:60-65](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/get-pkg-manager.ts#L60-L65)

### Dependency Installation and Network Fallbacks

Dependency installation is managed by the `install` helper, which spawns the chosen package manager with the `install` argument. When `isOnline` evaluates to `false`, the `--offline` flag is appended to force resolution from local caches.

```typescript
export async function install(
  /** Indicate which package manager to use. */
  packageManager: PackageManager,
  /** Indicate whether there is an active Internet connection.*/
  isOnline: boolean
): Promise<void> {
  const args: string[] = ['install']
  if (!isOnline) {
    console.log(
      yellow('You appear to be offline.\nFalling back to the local cache.')
    )
    args.push('--offline')
  }
```
Sources: [packages/create-next-app/helpers/install.ts:11-23](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/install.ts#L11-L23)

> [!WARNING]
> Spawning child processes forces `NODE_ENV` to `development` within the execution environment payload to prevent `pnpm` from prematurely pruning development dependencies required during project compilation.

```typescript
const child = spawn(packageManager, args, {
  stdio: 'inherit',
  env: {
    ...process.env,
    ADBLOCK: '1',
    // we set NODE_ENV to development as pnpm skips dev
    // dependencies when production
    NODE_ENV: 'development',
    DISABLE_OPENCOLLECTIVE: '1',
  },
})
```
Sources: [packages/create-next-app/helpers/install.ts:31-41](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/install.ts#L31-L41)

If the installation process exits with a non-zero code, the child handler rejects with an object encapsulating the failing command string.

```typescript
child.on('close', (code) => {
  if (code !== 0) {
    reject({ command: `${packageManager} ${args.join(' ')}` })
    return
  }
  resolve()
})
```
Sources: [packages/create-next-app/helpers/install.ts:42-48](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/install.ts#L42-L48)

### Post-Scaffolding Type Generation

After remote example repositories complete their package installation phase, the orchestrator triggers `runTypegen` as a best-effort operation to synthesize initial TypeScript declaration linkages.

```typescript
  try {
    console.log()
    await runTypegen(packageManager)
    console.log()
  } catch (err) {
    // Best effort: do not fail app creation if typegen fails
    console.error('Error running typegen:', err)
  }
}
```
Sources: [packages/create-next-app/create-app.ts:233-241](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts#L233-L241)

## Git Initialization and Lifecycle Finalization

### Repository Initialization Checks

The `tryGitInit` helper function orchestrates local version control setup by checking for existing repository boundaries. Before running any commands, it verifies whether the destination path resides within an existing Git repository or a Mercurial repository by executing `git rev-parse --is-inside-work-tree` and `hg --cwd . root` respectively.

```typescript
function isInGitRepository(): boolean {
  try {
    execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' })
    return true
  } catch (_) {}
  return false
}

function isInMercurialRepository(): boolean {
  try {
    execSync('hg --cwd . root', { stdio: 'ignore' })
    return true
  } catch (_) {}
  return false
}
```
Sources: [packages/create-next-app/helpers/git.ts:5-19](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/git.ts#L5-L19)

> [!WARNING]
> If `tryGitInit` detects that the target directory is already part of an active Git or Mercurial working tree, initialization is bypassed entirely and the function returns `false` to prevent nested repository corruption.

```typescript
if (isInGitRepository() || isInMercurialRepository()) {
  return false
}
```
Sources: [packages/create-next-app/helpers/git.ts:33-35](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-App/helpers/git.ts#L33-L35)

### Default Branch and Initial Commit

When no repository exists and Git is available on the host system, the orchestrator invokes `git init`. It checks whether a default branch name has been configured globally via `git config init.defaultBranch`. If unconfigured, it explicitly switches branches to `main` using `git checkout -b main`.

```typescript
function isDefaultBranchSet(): boolean {
  try {
    execSync('git config init.defaultBranch', { stdio: 'ignore' })
    return true
  } catch (_) {}
  return false
}

export function tryGitInit(root: string): boolean {
  let didInit = false
  try {
    execSync('git --version', { stdio: 'ignore' })
    if (isInGitRepository() || isInMercurialRepository()) {
      return false
    }

    execSync('git init', { stdio: 'ignore' })
    didInit = true

    if (!isDefaultBranchSet()) {
      execSync('git checkout -b main', { stdio: 'ignore' })
    }
```
Sources: [packages/create-next-app/helpers/git.ts:21-42](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/git.ts#L21-L42)

Following branch selection, all scaffolded project files are staged via `git add -A` and committed with the message `"Initial commit from Create Next App"`. If any step in this sequence throws an exception after initialization has started, the helper cleans up by recursively removing the `.git` directory using `rmSync`.

```typescript
  execSync('git add -A', { stdio: 'ignore' })
  execSync('git commit -m "Initial commit from Create Next App"', {
    stdio: 'ignore',
  })
  return true
} catch (e) {
  if (didInit) {
    try {
      rmSync(join(root, '.git'), { recursive: true, force: true })
    } catch (_) {}
  }
  return false
}
```
Sources: [packages/create-next-app/helpers/git.ts:44-56](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/helpers/git.ts#L44-L56)

### Completion Messaging and Next Steps

Once scaffolding, installation, and optional Git initialization complete, the orchestrator computes the correct relative path (`cdpath`) for navigating into the newly created directory. It outputs a success message followed by a list of available package manager commands tailored to the user's configuration.

```typescript
let cdpath: string
if (join(originalDirectory, appName) === appPath) {
  cdpath = appName
} else {
  cdpath = appPath
}

console.log(`${green('Success!')} Created ${appName} at ${appPath}`)

if (hasPackageJson) {
  console.log('Inside that directory, you can run several commands:')
  console.log()
  console.log(cyan(`  ${packageManager} ${useYarn ? '' : 'run '}dev`))
  console.log('    Starts the development server.')
  console.log()
  console.log(cyan(`  ${packageManager} ${useYarn ? '' : 'run '}build`))
  console.log('    Builds the app for production.')
  console.log()
  console.log(cyan(`  ${packageManager} start`))
  console.log('    Runs the built app in production mode.')
  console.log()
  console.log('We suggest that you begin by typing:')
  console.log()
  console.log(cyan('  cd'), cdpath)
  console.log(`  ${cyan(`${packageManager} ${useYarn ? '' : 'run '}dev`)}`)
}
```
Sources: [packages/create-next-app/create-app.ts:280-305](https://github.com/blade47/next.js/blob/d8c1c1b0002db8ccb63b58095f338e8bf423fa99/packages/create-next-app/create-app.ts#L280-L305)

## Related

- [Quick Start](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/quick-start)
- [Monorepo Workspace](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/orientation/monorepo-workspace)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
