Skip to content

Next.js — Guía rápida para crear un proyecto desde cero (con ejemplos)

Notifications You must be signed in to change notification settings

corbat-tech/corbat-nextjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

Next.js — Guía rápida para crear un proyecto desde cero (con ejemplos)

Este README explica cómo crear una aplicación Next.js desde cero, los comandos clave y una introducción práctica a su estructura y conceptos (enrutado, componentes, datos, estilos, API, etc.). Está pensado para que cualquiera pueda arrancar y entender lo básico de un proyecto Next.js moderno (App Router).

Requisitos recomendados: Node.js ≥ 18.18 (o 20+), PNPM ≥ 9 (o NPM/Yarn). Este README usa pnpm por defecto e incluye variantes con npm cuando es útil.


Índice

  1. Instalación de Node.js y gestor de paquetes
  2. Crear un nuevo proyecto Next.js
  3. Pasos iniciales después de crear el proyecto
  4. Instalación de librerías recomendadas
  5. Estructura básica del proyecto
  6. Scripts habituales
  7. App Router: rutas, layouts y páginas
  8. Componentes: Server vs Client
  9. Data Fetching y caché
  10. API Routes (Route Handlers)
  11. Estilos: CSS Modules, Tailwind, etc.
  12. Assets, fuentes e imágenes
  13. Variables de entorno
  14. Configuración: next.config.ts y tsconfig.json
  15. Linter, formateo y calidad
  16. Git, .gitignore y primer commit
  17. Ejecutar en local y compilar para producción
  18. Despliegue (Vercel y otras opciones)
  19. FAQ breve

1) Instalación de Node.js y gestor de paquetes

Asegúrate de tener Node.js instalado.

# Ver versiones instaladas
node -v
npm -v

# (Opcional) Instalar pnpm de forma global
npm install -g pnpm

# Ver versión de pnpm
pnpm -v

¿Por qué PNPM? Usa un almacén global eficiente y enlaces duros, ahorrando espacio y acelerando instalaciones. Puedes usar npm si prefieres simplicidad o no quieres instalar nada adicional.


2) Crear un nuevo proyecto Next.js

Usaremos el generador oficial create-next-app.

# Con pnpm (recomendado)
pnpm create next-app@latest my-app

# Alternativa con npm
npm create next-app@latest my-app
# o 
npx create-next-app@latest my-app

Durante el asistente podrás elegir:

  • TypeScript (sí, recomendado)
  • ESLint (sí)
  • Tailwind CSS (opcional pero recomendado)
  • App Router (sí — es el router moderno de Next.js)
  • /src directory (opcional; mantener la raíz simple también es válido)
  • Import alias (por ejemplo @/*)

3) Pasos iniciales después de crear el proyecto

Después de ejecutar create-next-app, sigue estos pasos antes de pasar a la estructura del proyecto. Aquí añadimos además paquetes recomendados y la configuración mínima de calidad para que el proyecto quede listo desde el primer día.

# 3.1 Entra al directorio del proyecto
cd my-app

# 3.2 Instala dependencias (si el asistente no lo hizo)
pnpm install   # o npm install

# 3.3 Arranca en desarrollo para verificar
pnpm dev       # o npm run dev
# abre http://localhost:3000

3.4 Paquetes recomendados (UI, i18n, formularios, email, utilidades)

Por qué: Estos paquetes cubren casos comunes (internacionalización, UI, toasts, formularios/validación, emails) y utilidades frecuentes.

pnpm add next-intl@^3 lucide-react sonner react-hook-form zod resend clsx
  • next-intl@^3: i18n para App Router (routing por locales, traducciones, formateo).
  • lucide-react: set de iconos moderno.
  • sonner: notificaciones/toasts minimalistas.
  • react-hook-form + zod: formularios con validación tipada.
  • resend: envío de emails (ideal con Route Handlers + Resend).
  • clsx: composición de clases CSS sencilla.

3.5 Testing + formateo + hooks de Git

Por qué: garantizar calidad (tests), estilo consistente (Prettier) y ejecutar checks automáticos en cada commit (Husky + lint-staged).

pnpm add -D vitest @testing-library/react @testing-library/jest-dom \
  @types/testing-library__jest-dom \
  husky lint-staged prettier prettier-plugin-tailwindcss

Nota: @vitejs/plugin-react no es necesario para Next.js. Se usa con proyectos Vite. Si vas a probar componentes en un sandbox Vite, entonces sí tendría sentido añadirlo.

Inicializa Husky (con pnpm):

pnpm dlx husky-init && pnpm exec husky install
# crea .husky/pre-commit que ejecuta "npm test" por defecto; lo adaptamos abajo

Si prefieres lo que usaste antes: npx husky init también funciona, pero con pnpm se recomienda pnpm dlx husky-init.

Configura lint-staged y scripts en package.json (añade o ajusta):

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "vitest run",
    "test:ui": "vitest"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": ["next lint --fix --file", "prettier --write"],
    "*.{md,css,json}": ["prettier --write"]
  }
}

Ajusta el hook pre-commit de Husky (.husky/pre-commit):

#!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"

pnpm exec lint-staged
pnpm run test --run

Crea .prettierrc si no existe:

printf '{
  "singleQuote": true,
  "semi": true,
  "plugins": ["prettier-plugin-tailwindcss"]
}
' > .prettierrc

Config mín. para Vitest (vitest.config.ts):

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'jsdom',
    setupFiles: ['./vitest.setup.ts']
  }
});

Archivo de setup (vitest.setup.ts):

import '@testing-library/jest-dom';

Ejemplo de test (components/Counter.test.tsx):

import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('increments counter', () => {
  render(<Counter />);
  const button = screen.getByRole('button');
  fireEvent.click(button);
  expect(button).toHaveTextContent('Clicks: 1');
});

3.6 shadcn/ui (opcional pero práctico)

Por qué: Librería de componentes sobre Tailwind, centrada en accesibilidad y composabilidad.

Requiere Tailwind. Si lo marcaste en create-next-app, ya está listo. Si no, añade Tailwind y configúralo antes.

Inicializa shadcn/ui:

pnpm dlx shadcn-ui@latest init -y

Añade un componente de ejemplo (Button):

pnpm dlx shadcn-ui@latest add button

Uso rápido:

import { Button } from "@/components/ui/button";

export default function Demo() {
  return <Button>Click me</Button>;
}

3.7 Ejemplos rápidos con los paquetes añadidos

next-intl (App Router) — estructura mínima

// app/[locale]/layout.tsx
import { NextIntlClientProvider, useMessages } from 'next-intl';
import type { ReactNode } from 'react';

export default function LocaleLayout({ children, params }: { children: ReactNode; params: { locale: string } }) {
  const messages = useMessages();
  return (
    <html lang={params.locale}>
      <body>
        <NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
      </body>
    </html>
  );
}
// app/[locale]/page.tsx
import { getTranslations } from 'next-intl/server';

export default async function Home() {
  const t = await getTranslations('Home');
  return <h1>{t('title')}</h1>; // Home.title en tus mensajes
}

Mensajes

# messages/es.json
{ "Home": { "title": "Hola Next.js" } }
# messages/en.json
{ "Home": { "title": "Hello Next.js" } }

Formulario con RHF + Zod

'use client';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';

const schema = z.object({ email: z.string().email(), name: z.string().min(2) });

type FormData = z.infer<typeof schema>;

export default function SimpleForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({ resolver: zodResolver(schema) });
  const onSubmit = (data: FormData) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input placeholder="Email" {...register('email')} />
      {errors.email && <p>{errors.email.message}</p>}
      <input placeholder="Name" {...register('name')} />
      {errors.name && <p>{errors.name.message}</p>}
      <button type="submit">Send</button>
    </form>
  );
}

Toasts con sonner

'use client';
import { Toaster, toast } from 'sonner';

export default function ToastDemo() {
  return (
    <div>
      <button onClick={() => toast.success('Saved!')}>Show toast</button>
      <Toaster richColors />
    </div>
  );
}

Email con Resend

// app/api/send-email/route.ts
import { Resend } from 'resend';
import { NextResponse } from 'next/server';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(request: Request) {
  const { to, subject, html } = await request.json();
  const result = await resend.emails.send({ from: 'MyApp <onboarding@myapp.dev>', to, subject, html });
  return NextResponse.json(result);
}

Con esto, el proyecto queda listo para trabajar (i18n, UI, formularios, toasts, emails, tests, formateo y hooks de calidad).


4) Instalación de librerías recomendadas

Para proyectos reales conviene añadir librerías de internacionalización, formularios, validación, notificaciones, testing y tooling. Aquí una lista base:

# Utilidades principales
pnpm add next-intl@^3 lucide-react sonner react-hook-form zod resend clsx
  • next-intl: internacionalización (traducciones)
  • lucide-react: iconos SVG
  • sonner: notificaciones
  • react-hook-form: gestión de formularios
  • zod: validación de datos
  • resend: envío de emails
  • clsx: concatenar clases de forma segura
# Dependencias de desarrollo (testing, formateo y calidad)
pnpm add -D vitest @testing-library/react @testing-library/jest-dom \
  @types/testing-library__jest-dom @vitejs/plugin-react \
  husky lint-staged prettier prettier-plugin-tailwindcss
  • vitest: framework de tests rápido
  • Testing Library: utilidades para testear componentes
  • Husky + lint-staged: ejecutar checks en pre-commit
  • Prettier + plugin tailwind: formateo consistente
# UI Components con shadcn
pnpm dlx shadcn-ui@latest init -y
  • shadcn-ui: sistema de componentes accesibles + tailwind
# Inicializar husky (hooks de git)
npx husky init

Con esto el proyecto queda listo para desarrollo real: buenas prácticas de DX, testing, estilos consistentes y componentes reutilizables.


5) Estructura básica del proyecto

Con App Router, la estructura típica es:

my-app/
├─ app/
│  ├─ layout.tsx        # Layout raíz (envoltorio de todas las páginas)
│  ├─ page.tsx          # Página para ruta "/"
│  ├─ about/
│  │  └─ page.tsx       # Página para ruta "/about"
│  ├─ api/              # Rutas de API (route handlers)
│  │  └─ hello/route.ts # Endpoint en "/api/hello"
│  └─ (marketing)/      # Segmentos con nombre (agrupación lógica)
├─ public/              # Archivos estáticos (imágenes, favicon, etc.)
├─ styles/              # Estilos globales o base (si se generan)
├─ components/          # Componentes reutilizables
├─ lib/                 # Utilidades, servicios, clientes http, etc.
├─ next.config.ts       # Configuración de Next.js
├─ package.json
├─ pnpm-lock.yaml       # o package-lock.json / yarn.lock
└─ tsconfig.json        # Config de TypeScript

¿Qué es app/? Define rutas por convención de carpetas y fomenta componentes de servidor por defecto. Cada carpeta puede tener:

  • page.tsx: Página renderizable en esa ruta.
  • layout.tsx: Layout que envuelve a las page.tsx hijas.
  • loading.tsx: UI de carga para la ruta.
  • error.tsx: UI de error para la ruta.
  • route.ts: Endpoint HTTP (si está dentro de app/api/...).
  • template.tsx: Similar a layout, pero se recrea en cada navegación.

6) Scripts habituales

En package.json encontrarás scripts como:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  }
}

Comandos:

# desarrollo con HMR
dpnm dev   # (o) pnpm dev
npm run dev

# compilar para producción
pnpm build
npm run build

# arrancar en modo producción (tras build)
pnpm start
npm run start

# ejecutar linter
pnpm lint

Nota: para ver la app en local, abre http://localhost:3000.


7) App Router: rutas, layouts y páginas

Página simple (app/page.tsx)

// app/page.tsx
export default function HomePage() {
  return (
    <main>
      <h1>Hello Next.js</h1>
      <p>Esta es la home (ruta "/").</p>
    </main>
  );
}

Layout raíz (app/layout.tsx)

Define HTML y UI compartida.

// app/layout.tsx
import type { Metadata } from 'next';
import './globals.css';

export const metadata: Metadata = {
  title: 'My App',
  description: 'Demo Next.js',
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="es">
      <body>
        <header>My App</header>
        {children}
        <footer>© {new Date().getFullYear()}</footer>
      </body>
    </html>
  );
}

Crear una ruta /about

mkdir app/about && printf "export default function About(){return <h2>About</h2>}" > app/about/page.tsx

Navegación (enlace entre páginas)

// components/Nav.tsx
import Link from 'next/link';

export default function Nav() {
  return (
    <nav>
      <Link href="/">Home</Link> | <Link href="/about">About</Link>
    </nav>
  );
}

Incluye Nav en tu layout o páginas para que aparezca en todas.


8) Componentes: Server vs Client

  • Server Components (por defecto): se renderizan en el servidor, mejor rendimiento, pueden acceder a secretos y hacer fetch directamente.
  • Client Components: necesarios para interactividad (estado, efectos, eventos). Se declaran con la directiva 'use client' al inicio del archivo.
// components/Counter.tsx
'use client';
import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount((c) => c + 1)}>Clicks: {count}</button>
  );
}

Usa componentes de servidor cuando no necesites estado/eventos y componentes cliente para UI interactiva.


9) Data Fetching y caché

En App Router, puedes hacer fetch directamente en componentes server. Next gestiona la caché, revalidación y renderizado.

// app/users/page.tsx
async function getUsers() {
  const res = await fetch('https://jsonplaceholder.typicode.com/users', {
    // revalidar cada 60s (ISR)
    next: { revalidate: 60 },
  });
  if (!res.ok) throw new Error('Error al cargar usuarios');
  return res.json() as Promise<Array<{ id: number; name: string }>>;
}

export default async function UsersPage() {
  const users = await getUsers();
  return (
    <main>
      <h1>Usuarios</h1>
      <ul>
        {users.map((u) => (
          <li key={u.id}>{u.name}</li>
        ))}
      </ul>
    </main>
  );
}

Modos de fetch y caché (resumen):

  • Estático (por defecto): la respuesta se cachea en build.
  • ISR: usa next: { revalidate: N } para regenerar cada N segundos.
  • Dinámico: añade cache: 'no-store' o lee cookies()/headers() para desactivar caché.
await fetch(url, { cache: 'no-store' }); // Fuerza dinámico

10) API Routes (Route Handlers)

Puedes crear endpoints dentro de app/api/**/route.ts.

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  return NextResponse.json({ message: 'Hello from API' });
}

export async function POST(request: Request) {
  const body = await request.json();
  return NextResponse.json({ ok: true, received: body }, { status: 201 });
}
  • Soporta métodos GET, POST, PUT, PATCH, DELETE.
  • Se ejecuta en Edge o Node según configuración (por defecto Node). Puedes exportar runtime = 'edge'.

11) Estilos: CSS Modules, Tailwind, etc.

Opciones comunes:

  • CSS Modules: scoping por archivo (.module.css).
  • Tailwind CSS: utilidades con clases; rápido para prototipado.
  • Styled JSX/Styled Components/Vanilla Extract: otras alternativas.

Ejemplo CSS Modules

// components/Box.tsx
import styles from './Box.module.css';
export default function Box() {
  return <div className={styles.box}>Caja</div>;
}
/* components/Box.module.css */
.box { padding: 1rem; border: 1px solid #ddd; border-radius: 8px; }

Ejemplo Tailwind (si lo activaste)

export default function Card({ title }: { title: string }) {
  return (
    <div className="rounded-xl border p-4 shadow-sm">
      <h3 className="text-lg font-semibold">{title}</h3>
    </div>
  );
}

12) Assets, fuentes e imágenes

  • Coloca estáticos en public/ y referencia con /archivo.png.
  • Usa next/image para imágenes optimizadas.
  • Usa next/font para cargar fuentes locales o de Google.
// Ejemplo next/image
import Image from 'next/image';

export default function Avatar() {
  return <Image src="/avatar.png" alt="Avatar" width={64} height={64} />;
}
// Ejemplo next/font (Google)
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });

export default function Text() {
  return <p className={inter.className}>Texto con Inter</p>;
}

13) Variables de entorno

Crea archivos .env y .env.local (no subas secretos a git). Para exponer variables al cliente deben empezar por NEXT_PUBLIC_.

# .env.local (ejemplo)
DATABASE_URL=postgres://...
NEXT_PUBLIC_API_BASE=/api

Lee variables en server components o route handlers con process.env.X.


14) Configuración: next.config.ts y tsconfig.json

next.config.ts (ejemplo mínimo)

import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  reactStrictMode: true,
  images: {
    remotePatterns: [
      { protocol: 'https', hostname: 'images.example.com' }
    ]
  }
};

export default nextConfig;

tsconfig.json (extracto típico)

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./*"] },
    "strict": true,
    "jsx": "preserve",
    "esModuleInterop": true,
    "moduleResolution": "bundler"
  }
}

15) Linter, formateo y calidad

  • ESLint: reglas de calidad (incluye plugin de Next por defecto).
  • Prettier: formateo consistente.
# Instalar Prettier (si no venía)
pnpm add -D prettier

# .prettierrc (ejemplo)
printf '{\n  "singleQuote": true,\n  "semi": true\n}\n' > .prettierrc

# Ejecutar
pnpm lint

Consejo: configura hooks con Husky + lint-staged para validar antes de cada commit.


16) Git, .gitignore y primer commit

git init

# .gitignore recomendado (si no lo generó el asistente)
printf "node_modules\n.next\n.env*\n.vercel\n" > .gitignore

git add .
git commit -m "chore: bootstrap Next.js app"

git branch -M main
# Crea el repo en GitHub y vincula
# git remote add origin git@github.com:usuario/my-app.git
# git push -u origin main

17) Ejecutar en local y compilar para producción

# desarrollo
pnpm dev

# build producción
pnpm build

# ejecutar build
pnpm start
  • Dev por defecto: http://localhost:3000.
  • next build hace análisis, tree-shaking y prepara artefactos optimizados.

18) Despliegue (Vercel y otras opciones)

Vercel (opción más simple y nativa):

# Instalar CLI (opcional)
pnpm add -g vercel

# Deploy interactivo desde la carpeta del proyecto
vercel

# Para produccion
vercel --prod

Otras opciones: contenedor Docker + Node, plataformas como Netlify, Render, Fly.io, Railway, etc. En Node, ejecuta next build y sirve con next start detrás de un reverse proxy.


19) FAQ breve

¿App Router o Pages Router? Usa App Router (carpeta app/) para proyectos nuevos. pages/ sigue soportado pero es el enfoque legado.

¿TypeScript obligatorio? No, pero es muy recomendado por DX y robustez.

¿PNPM vs NPM? PNPM suele ser más rápido y ahorra espacio. NPM viene con Node y es suficiente si prefieres simplicidad.

¿Cómo organizo componentes y lógica? Crea carpetas components/ y lib/ para separar UI de utilidades/servicios. Reutiliza layouts y usa Server Components por defecto.


Resumen de comandos clave

# Crear proyecto
pnpm create next-app@latest my-app
cd my-app
pnpm install

# Desarrollo
pnpm dev

# Linter
pnpm lint

# Build + start prod
pnpm build
pnpm start

# (Opcional) Tailwind se incluye si lo seleccionaste en el asistente

# Git básico
git init && git add . && git commit -m "chore: bootstrap"

Apéndice: ejemplos extra rápidos

loading y error en una ruta

// app/users/loading.tsx
export default function Loading() {
  return <p>Cargando usuarios…</p>;
}

// app/users/error.tsx
'use client';
export default function Error({ error }: { error: Error }) {
  return <p>Ha ocurrido un error: {error.message}</p>;
}

Parámetros de ruta

// app/blog/[slug]/page.tsx
export default function BlogPost({ params }: { params: { slug: string } }) {
  return <h1>Post: {params.slug}</h1>;
}

Búsqueda (search params)

// app/search/page.tsx
export default function SearchPage({ searchParams }: { searchParams: { q?: string } }) {
  return <h1>Buscando: {searchParams.q ?? '—'}</h1>;
}

About

Next.js — Guía rápida para crear un proyecto desde cero (con ejemplos)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published