Skip to content

Crear proyecto ​

Vas a crear tu primer proyecto de Next.js con la configuración recomendada (App Router, TypeScript, Tailwind y ESLint), entender cada archivo y carpeta que se genera, y conocer los comandos y la configuración básica.

Concepto ​

create-next-app es el asistente oficial: crea la estructura, instala dependencias y deja todo configurado. Next.js usa una convención clara: las carpetas dentro de app/ son las rutas de tu sitio.

Requisitos: Node.js 20.9 o superior (instalación).

Crear el proyecto ​

bash
npx create-next-app@latest mi-sitio

El asistente pregunta (respuestas recomendadas):

txt
✔ Would you like to use the recommended Next.js defaults? › No, customize settings
✔ Would you like to use TypeScript? › Yes
✔ Which linter would you like to use? › ESLint
✔ Would you like to use Tailwind CSS? › Yes
✔ Would you like your code inside a `src/` directory? › No
✔ Would you like to use App Router? (recommended) › Yes
✔ Would you like to use Turbopack? › Yes
✔ Would you like to customize the import alias (`@/*` by default)? › No

O en un solo comando sin preguntas:

bash
npx create-next-app@latest mi-sitio --ts --tailwind --eslint --app --use-npm --import-alias "@/*"
bash
cd mi-sitio
npm run dev

Abre http://localhost:3000.

Estructura generada ​

txt
mi-sitio/
├── app/                    ← rutas de tu app (App Router)
│   ├── favicon.ico
│   ├── globals.css         ← estilos globales (incluye Tailwind)
│   ├── layout.tsx          ← layout raíz: envuelve TODAS las páginas
│   └── page.tsx            ← página de inicio "/"
├── public/                 ← archivos estáticos servidos tal cual (/logo.svg)
├── next.config.ts          ← configuración de Next.js
├── tsconfig.json           ← configuración de TypeScript (incluye alias @/*)
├── eslint.config.mjs
├── postcss.config.mjs      ← Tailwind CSS v4
├── package.json
└── next-env.d.ts           ← tipos de Next (no lo edites)

Los archivos clave ​

tsx
import type { Metadata } from 'next'
import { Geist } from 'next/font/google'
import './globals.css'

// next/font descarga la fuente en el build y la sirve desde tu dominio
const geist = Geist({ subsets: ['latin'], variable: '--font-geist' })

// SEO por defecto para todo el sitio
export const metadata: Metadata = {
  title: 'Mi sitio',
  description: 'Creado con Next.js'
}

// El layout raíz es obligatorio y debe tener <html> y <body>
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es">
      <body className={`${geist.variable} antialiased`}>{children}</body>
    </html>
  )
}
tsx
// Página de inicio: corresponde a la ruta "/"
export default function Home() {
  return (
    <main className="mx-auto max-w-2xl p-8">
      <h1 className="text-4xl font-bold">¡Hola, Next.js! 👋</h1>
      <p className="mt-4 text-gray-600">
        Edita <code>app/page.tsx</code> y guarda para ver los cambios.
      </p>
    </main>
  )
}
css
@import "tailwindcss";

:root {
  --background: #ffffff;
  --foreground: #171717;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0a0a0a;
    --foreground: #ededed;
  }
}

body {
  background: var(--background);
  color: var(--foreground);
}
ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  /* opciones de configuración */
}

export default nextConfig
json
{
  "name": "mi-sitio",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "start": "next start",
    "lint": "eslint"
  },
  "dependencies": {
    "next": "^16.0.0",
    "react": "^19.2.0",
    "react-dom": "^19.2.0"
  },
  "devDependencies": {
    "@tailwindcss/postcss": "^4",
    "@types/node": "^22",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "^16.0.0",
    "tailwindcss": "^4",
    "typescript": "^5"
  }
}

Versiones

Next.js publica versiones mayores aproximadamente una vez al año. Este curso es válido para Next.js 15 y 16: la estructura del App Router, Server Components y Server Actions son iguales. Cuando haya diferencias importantes entre versiones, lo indicaremos.

Comandos ​

ComandoQué hace
npm run devServidor de desarrollo con recarga instantánea (Turbopack)
npm run buildCompila la app optimizada para producción
npm startSirve el build de producción (requiere npm run build antes)
npm run lintRevisa errores con ESLint

Al ejecutar npm run build verás un resumen de tus rutas:

txt
Route (app)                    Size     First Load JS
┌ ○ /                          5.2 kB        107 kB
├ ○ /nosotros                  142 B         102 kB
└ ƒ /dashboard                 1.1 kB        103 kB

○  (Static)   prerendered as static content
ƒ  (Dynamic)  server-rendered on demand

Tu primera ruta nueva ​

Crear una ruta es crear una carpeta con un archivo page.tsx:

txt
app/
├── page.tsx              → /
├── nosotros/
│   └── page.tsx          → /nosotros
└── servicios/
    ├── page.tsx          → /servicios
    └── diseno-web/
        └── page.tsx      → /servicios/diseno-web
tsx
import Link from 'next/link'

export default function Nosotros() {
  return (
    <main className="mx-auto max-w-2xl p-8">
      <h1 className="text-3xl font-bold">Nosotros</h1>
      <p className="mt-4">Somos un equipo apasionado por la web.</p>
      {/* Link navega sin recargar y precarga la página destino */}
      <Link href="/" className="mt-6 inline-block text-violet-700 underline">
        ← Volver al inicio
      </Link>
    </main>
  )
}
tsx
import Link from 'next/link'

export default function Home() {
  return (
    <main className="mx-auto max-w-2xl p-8">
      <h1 className="text-4xl font-bold">Bienvenido</h1>
      <nav className="mt-6 flex gap-4">
        <Link href="/nosotros" className="text-violet-700 underline">Nosotros</Link>
        <Link href="/servicios" className="text-violet-700 underline">Servicios</Link>
      </nav>
    </main>
  )
}

Usa siempre Link de next/link para navegar dentro de tu sitio: evita recargas y precarga automáticamente las páginas de los enlaces visibles.

Organizar el código ​

Sólo las carpetas con page.tsx (o route.ts) se vuelven rutas públicas. Puedes guardar otros archivos dentro de app/ sin problema, pero es común tener carpetas al nivel raíz:

txt
mi-sitio/
├── app/              ← sólo rutas, layouts y archivos especiales
├── components/       ← componentes reutilizables (Header, Boton...)
├── lib/              ← utilidades, cliente de base de datos, validaciones
├── public/
└── ...

Gracias al alias @/* configurado en tsconfig.json, importas sin rutas relativas largas:

tsx
import Header from '@/components/Header'
import { formatearPrecio } from '@/lib/formato'

También puedes crear carpetas privadas dentro de app/ con guion bajo (app/_components/): Next.js las ignora como rutas.

Variables de entorno ​

Crea .env.local en la raíz (ya está en .gitignore):

env
# Sólo en el servidor (seguras para secretos)
DATABASE_URL="postgresql://usuario:password@localhost:5432/mi_sitio"
API_SECRET="clave-super-secreta"

# Visibles en el navegador: prefijo NEXT_PUBLIC_
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
tsx
// En un Server Component o Server Action: cualquier variable
const secreto = process.env.API_SECRET

// En un Client Component: sólo las NEXT_PUBLIC_
const url = process.env.NEXT_PUBLIC_SITE_URL

Seguridad

Las variables NEXT_PUBLIC_ se incrustan en el JavaScript público. Nunca pongas secretos con ese prefijo.

Extensiones recomendadas ​

  • ESLint y Tailwind CSS IntelliSense para VS Code.
  • React Developer Tools en el navegador.
  • Prettier con prettier-plugin-tailwindcss para ordenar las clases automáticamente:
bash
npm i -D prettier prettier-plugin-tailwindcss
json
// .prettierrc
{ "semi": false, "singleQuote": true, "plugins": ["prettier-plugin-tailwindcss"] }

Buena práctica

Mantén app/ enfocado en rutas. Los componentes reutilizables van en components/ y la lógica (acceso a datos, validaciones) en lib/. Te lo agradecerás cuando el proyecto crezca.

Error común

Usar etiquetas a normales para enlaces internos. Funcionan, pero recargan toda la página y pierdes la precarga. Usa Link (y a sólo para enlaces externos).

Ejercicios ​

  1. Crea un proyecto agencia y agrega las rutas /servicios, /portafolio y /contacto, cada una con un título y un enlace de regreso al inicio.
  2. Crea un componente components/Navbar.tsx con enlaces a todas las páginas y agrégalo al layout raíz para que aparezca en todo el sitio.
Ver solución
tsx
// components/Navbar.tsx
import Link from 'next/link'

const enlaces = [
  { href: '/', texto: 'Inicio' },
  { href: '/servicios', texto: 'Servicios' },
  { href: '/portafolio', texto: 'Portafolio' },
  { href: '/contacto', texto: 'Contacto' }
]

export default function Navbar() {
  return (
    <header className="border-b">
      <nav className="mx-auto flex max-w-5xl gap-6 p-4">
        <span className="font-bold text-violet-700">Agencia</span>
        {enlaces.map((e) => (
          <Link key={e.href} href={e.href} className="hover:text-violet-700">
            {e.texto}
          </Link>
        ))}
      </nav>
    </header>
  )
}
tsx
// app/layout.tsx
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import './globals.css'

export const metadata: Metadata = { title: 'Agencia', description: 'Agencia de desarrollo web' }

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es">
      <body>
        <Navbar />
        {children}
      </body>
    </html>
  )
}
tsx
// app/servicios/page.tsx (igual para portafolio y contacto)
import Link from 'next/link'

export default function Servicios() {
  return (
    <main className="mx-auto max-w-5xl p-8">
      <h1 className="text-3xl font-bold">Servicios</h1>
      <Link href="/" className="mt-4 inline-block underline">← Inicio</Link>
    </main>
  )
}

Resumen ​

  • npx create-next-app@latest crea un proyecto con App Router, TypeScript y Tailwind.
  • app/layout.tsx envuelve todas las páginas; cada carpeta con page.tsx es una ruta.
  • npm run dev para desarrollar; npm run build + npm start para producción.
  • Navega con Link; variables secretas en .env.local y públicas con NEXT_PUBLIC_.

Academia Web — aprende, copia y construye.