Configuración básica de Next.js
La configuración más mínima de Next.js 15 — create-next-app bare con TypeScript y el App Router, nada más. Sin Tailwind, sin ESLint, sin directorio src/. Solo lo esencial para un proyecto dirigido por Server Components en React 19.
Receta
Tarjeta de receta de referencia rápida — lista para copiar y pegar.
# Crea la app solo con los flags que queremos
npx create-next-app@latest my-app \
--typescript \
--app \
--no-tailwind \
--no-eslint \
--no-src-dir \
--import-alias "@/*"
# Entra en el proyecto e inicia el servidor dev
cd my-app
npm run devCuándo usarlo: Aprender Next.js, escribir una repro de bug mínima reproducible, o iniciar un proyecto donde planeas agregar tu propia herramienta (Biome, Vanilla Extract, tu propio linter) en lugar de aceptar los valores predeterminados.
Ejemplo práctico
Después de que se complete la receta anterior, tendrás este diseño de directorio:
my-app/
├── app/
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx
├── public/
│ ├── next.svg
│ └── vercel.svg
├── next.config.ts
├── next-env.d.ts
├── package.json
├── tsconfig.json
└── README.mdEl layout raíz es un Server Component y envuelve cada ruta:
// app/layout.tsx
import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "My App",
description: "Minimal Next.js 15 + React 19 setup",
};
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}Una página Server Component que obtiene datos en tiempo de solicitud — sin useEffect, sin costo de bundle del cliente:
// app/page.tsx
type Post = { id: number; title: string };
async function getPosts(): Promise<Post[]> {
const res = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5", {
// Next 15 no tiene almacenamiento en caché automático para fetch; opta explícitamente
next: { revalidate: 60 },
});
if (!res.ok) throw new Error("Failed to fetch posts");
return res.json();
}
export default async function HomePage() {
const posts = await getPosts();
return (
<main>
<h1>Latest posts</h1>
<ul>
{posts.map((p) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
</main>
);
}El tsconfig.json generado activa el modo estricto y conecta el alias @/*:
// tsconfig.json (abreviado)
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"jsx": "preserve",
"incremental": true,
"plugins": [\{ "name": "next" \}],
"paths": \{ "@/*": ["./*"] \}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}Y el archivo de configuración ahora es TypeScript por defecto:
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Agrega configuración aquí según sea necesario.
};
export default nextConfig;Análisis profundo
Cómo funciona
create-next-app es un pequeño scaffolder distribuido como su propio paquete npm. Cuando ejecutas npx create-next-app@latest, npx descarga la última versión publicada en un caché temporal, la ejecuta y pasa los argumentos restantes. El scaffolder copia un directorio de plantilla del interior del paquete, ejecuta la instalación del gestor de paquetes elegido y (a menos que --disable-git esté configurado) ejecuta git init con un commit inicial.
Cada flag en la receta se asigna 1:1 a una respuesta de solicitud interactiva, por lo que --typescript --app --no-tailwind --no-eslint --no-src-dir es lo mismo que responder esas cinco solicitudes manualmente. Pasar los cinco significa que create-next-app se ejecuta de forma no interactiva y tu configuración es reproducible en CI.
Una vez instalado, npm run dev inicia el servidor dev de Next.js. En Next 15, Turbopack es el empaquetador dev predeterminado — no necesitas un flag separado.
Variaciones
- Turbopack dev (explícito): está activado por defecto, pero puedes forzarlo en
package.jsoncon"dev": "next dev --turbopack". - Puerto personalizado:
npm run dev -- -p 4000o establecePORT=4000 npm run dev. - Directorio personalizado: pasa cualquier ruta como nombre de proyecto, p. ej.
npx create-next-app@latest ./apps/web .... - Sin git init: agrega
--disable-gital comando. - Configuración manual:
mkdir my-app && cd my-app && npm init -y && npm install next@latest react@latest react-dom@latest && npm install -D typescript @types/react @types/node, luego creaapp/layout.tsxyapp/page.tsxmanualmente; Next generanext-env.d.tsen la primera ejecución. - pnpm:
pnpm create next-app my-app --typescript --app --no-tailwind --no-eslint --no-src-dir. - yarn:
yarn create next-app my-app --typescript --app --no-tailwind --no-eslint --no-src-dir. - bun:
bun create next-app my-app --typescript --app --no-tailwind --no-eslint --no-src-dir.
Notas de TypeScript
El flag --typescript escribe un tsconfig.json con strict: true, moduleResolution: "bundler" y el plugin de TypeScript de Next.js registrado bajo compilerOptions.plugins. Ese plugin es lo que impulsa el autocompletado para metadata, parámetros de ruta y los tipos de ruta generados en .next/types/.
La entrada paths "@/*": ["./*"] es relativa a la raíz del proyecto, no a src/, porque rechazaste --src-dir. Si posteriormente mueves archivos a src/, debes actualizar el alias a "@/*": ["./src/*"].
next-env.d.ts se genera automáticamente en cada dev/build. No lo edites; no lo elimines. El .gitignore estándar lo confirma.
Trampas
- App Router vs Pages Router. El directorio
app/es el App Router. Si también creas un directoriopages/, ambos routers se ejecutarán lado a lado y tendrás colisiones en rutas compartidas. Eliminapages/a menos que intencionalmente estés migrando. - Colocación de
src/vsapp/raíz. Con--no-src-dir, el App Router vive en./app. Si algún tutorial te dice que pongas archivos en./src/app, ese es el diseño--src-dir; mezclar los dos rompe silenciosamente las importaciones. - Los valores predeterminados cambian entre versiones de Next.js. En Next 15 las solicitudes predeterminadas incluyen Turbopack y
AGENTS.md— flags que no existían en Next 13 o 14. Siempre fija a@latesto una versión exacta en tu documentación. - Se requiere Node.js 18.18 o más nuevo. Next 15 se niega a iniciar en Node 18.17 e inferior. Ejecuta
node -vantes denpm run dev; usa nvm para cambiar si estás en un LTS más antiguo. - Puerto 3000 ya en uso.
npm run devfallará conEADDRINUSEsi otro proceso mantiene 3000. Mátalo (lsof -iTCP:3000 -sTCP:LISTEN) o ejecuta con-p 3001. fetchno se almacena en caché por defecto en Next 15. Si esperas el antiguo comportamiento "fetch se almacena en caché para siempre" de Next 13/14, debes optar explícitamente connext: \{ revalidate: N \}ocache: "force-cache".next.config.tsrequiere un runtime de Node consciente de TypeScript. Funciona lista para usar en Next 15, pero algunas herramientas más antiguas que ceden anext builddesde un script Node simple pueden quejarse — vuelve anext.config.mjssi encuentras esto.
Alternativas
| Herramienta | Cuándo usarla | Compensación vs Next.js 15 |
|---|---|---|
| Vite + React Router | SPAs, dashboards detrás de autenticación, sin necesidades SEO | Sin Server Components, sin SSR integrado, sin enrutamiento basado en sistema de archivos |
| Remix | Aplicaciones pesadas en formularios, enrutamiento anidado, mejora progresiva | Ecosistema más pequeño; modelo de carga de datos diferente (loader/action) |
| Astro | Sitios de contenido, páginas de marketing, blogs | React es un invitado; sin historia completa de React 19 Server Components |
| Next.js Pages Router | Bases de código heredadas, bibliotecas aún vinculadas a getServerSideProps | Siendo reemplazado por App Router; sin Server Components |
Preguntas frecuentes
Revisión rápida de esta página — haz clic para expandir.
¿Qué flag selecciona el App Router?
--app. Sin él, create-next-app puede usar por defecto el App Router en Next 15, pero el flag hace que la opción sea explícita e no interactiva.
¿Cómo me desuscribo de Tailwind y ESLint al mismo tiempo?
Pasa --no-tailwind --no-eslint a create-next-app. Ambos flags son requeridos porque cada uno está activado por defecto en Next 15.
¿Qué configura realmente --import-alias "@/*"?
Escribe una entrada paths en tsconfig.json para que @/lib/x se resuelva a ./lib/x (o ./src/lib/x si usaste --src-dir).
¿Por qué mi app/page.tsx es un Server Component por defecto?
Todo en app/ es un Server Component a menos que el archivo comience con "use client". Por eso puedes usar await dentro del cuerpo del componente.
¿Necesito un flag separado --turbopack en Next 15?
No. Turbopack es el empaquetador dev predeterminado. El flag solo es útil si deseas fijarlo en package.json para mayor claridad.
¿Cuál es la versión mínima de Node.js?
18.18.0. Cualquier cosa más antigua falla al iniciarse. Trampa: varias imágenes CI populares todavía envían 18.17 por defecto.
¿Qué sucede si el puerto 3000 está ocupado?
next dev sale con EADDRINUSE. Ejecuta npm run dev -- -p 3001 o mata el proceso que escucha en 3000. Trampa: algunas versiones más antiguas de Next incrementaron automáticamente el puerto; Next 15 no lo hace.
¿Es seguro editar next-env.d.ts?
No. Se regenera en cada compilación y cualquier edición se perderá. Déjalo confirmado e intacto.
¿Cómo habilito TypeScript estricto?
Ya está activado. create-next-app escribe "strict": true en tsconfig.json por defecto cuando pasas --typescript. Nota de TS: eso también habilita strictNullChecks, noImplicitAny y compañía.
¿Por qué mi llamada fetch golpea la red en cada solicitud?
Next 15 cambió el valor predeterminado: fetch ya no se almacena en caché a menos que optes por él. Agrega next: \{ revalidate: 60 \} o cache: "force-cache" a las opciones de fetch.
¿Puedo escribir next.config.ts con IntelliSense completo?
Sí. Importa NextConfig de next y anota la constante exportada. Nota de TS: NextConfig es el tipo canónico en Next 15; el comentario JSDoc más antiguo /** @type \{import('next').NextConfig\} */ ya no es necesario.
¿Cuál es la diferencia entre app/ y pages/?
app/ es el App Router (React 19, Server Components, layouts, streaming). pages/ es el Pages Router heredado. Puedes ejecutar ambos, pero inicia nuevos proyectos solo en app/.