¿Cuál es la estructura estándar de directorios de un proyecto Next.js?

Por 쉬었음.com

Una estructura estándar de directorios para un proyecto Next.js no es un único árbol de carpetas correcto que se aplique de manera idéntica a todos los equipos. En cambio, significa seguir convenciones definidas para los archivos de enrutamiento que el framework debe interpretar, al tiempo que se coloca el resto del código según la naturaleza del proyecto. Para un proyecto nuevo, un enfoque fácil de entender suele consistir en usar src/app como centro para las URL y los puntos de entrada de las páginas, separar en components la interfaz de usuario utilizada en varios lugares, colocar la lógica específica de funcionalidades en features cuando sea necesario y mantener las utilidades compartidas en lib. App Router es el enfoque principal de enrutamiento en las versiones actuales de Next.js, mientras que Pages Router sigue siendo compatible. nextjs.orgnextjs.org

Al definir una estructura por primera vez, lo importante no es crear tantas carpetas como sea posible. Primero, distingue qué archivos crean URL, cuáles gestionan layouts, pantallas de error y API, y qué código se utiliza únicamente dentro de una pantalla concreta. Este artículo examina una estructura mantenible centrada en App Router a partir de esas distinciones.

¿Por qué no existe una «única estructura estándar» en Next.js?

Next.js proporciona convenciones para crear rutas a partir de archivos y carpetas, pero no prescribe exactamente qué carpetas deben contener cada componente y cada elemento de lógica de negocio. En App Router en particular, las carpetas representan segmentos de URL, y archivos especiales como page.tsx y route.ts crean puntos de entrada públicos reales. En cambio, los archivos normales situados dentro de una ruta no se convierten en rutas externas de forma predeterminada. nextjs.org

Debido a esta característica, las estructuras pueden diferir incluso entre aplicaciones de Next.js. Un sitio de marketing pequeño puede necesitar únicamente app y unos pocos componentes compartidos. Por otro lado, en un servicio con múltiples dominios como paneles, pedidos y cuentas, separar el código específico de funcionalidades del código compartido facilita comprender el alcance de los cambios. No es una regla absoluta exigida por Next.js, sino un enfoque de organización del código que un equipo elige sobre las convenciones de enrutamiento.

Por tanto, resulta menos confuso pensar en «estándar» en las dos capas siguientes.

CategoríaNaturalezaEjemplos habituales
Convenciones interpretadas por Next.jsLos nombres y ubicaciones de los archivos afectan directamente al comportamientoapp/page.tsx, app/layout.tsx, app/api/users/route.ts
Convenciones definidas por el proyectoEl equipo define nombres y límites para sus propios finescomponents, features, lib, hooks, types

Cambiar arbitrariamente la primera capa puede modificar el enrutamiento o el comportamiento especial de la UI. La segunda capa se puede omitir o combinar según la escala y la complejidad del dominio. Por ejemplo, es posible tener una estructura sin features, y no es necesario segmentar en exceso cada parte del código en un proyecto pequeño.

¿Por qué los proyectos nuevos deberían diseñarse en torno a App Router?

App Router es un enfoque para crear páginas y layouts basado en el directorio app. La documentación oficial recomienda pasar de Pages Router a App Router para aprovechar las funciones más recientes de React, aunque Pages Router sigue siendo compatible. Por ello, debes comprender las convenciones de pages al mantener o aprender la estructura de un proyecto existente, pero es natural considerar App Router como la opción predeterminada al diseñar una estructura nueva. nextjs.org

La clave de App Router es conectar la jerarquía de URL con la jerarquía de archivos. Por ejemplo, app/dashboard/page.tsx se convierte en el punto de entrada de la página /dashboard, mientras que app/dashboard/layout.tsx puede proporcionar un layout aplicado a las subrutas del panel. El archivo raíz app/layout.tsx es el layout raíz que envuelve todas las rutas. nextjs.org

Este enfoque es adecuado para mantener cerca el código del nivel de pantalla. Las pestañas, tablas y la UI de filtros necesarias solo para /dashboard pueden residir en app/dashboard, mientras que los botones o campos de entrada reutilizados en varias pantallas pueden trasladarse a una carpeta compartida externa. El criterio clave no es «qué tecnología se utilizó para escribir este archivo», sino «dentro de qué alcance se reutiliza».

Sin embargo, usar App Router no significa que todo el código deba ir dentro de app. Puedes tratar app como un límite que facilita leer las URL y los archivos especiales, y separar el código compartido o las implementaciones complejas de funcionalidades en otras carpetas cuando sea necesario. Next.js no realiza esta separación automáticamente; es una convención estructural que el equipo debe definir de forma coherente.

¿Cuándo deberías usar una carpeta src y qué permanece en la raíz?

src es opcional. Cuando la utilizas, puedes reunir app y el código fuente de la aplicación dentro de src, separando visualmente los archivos de configuración del código de ejecución. En cambio, public, package.json, next.config.js, tsconfig.json y los archivos .env.* pertenecen a la raíz del proyecto. nextjs.org

El siguiente es un ejemplo fácil de entender al usar App Router junto con src.

my-app/
├─ public/
│  ├─ images/
│  └─ fonts/
├─ src/
│  ├─ app/
│  │  ├─ layout.tsx
│  │  ├─ page.tsx
│  │  ├─ globals.css
│  │  ├─ (marketing)/
│  │  │  └─ about/
│  │  │     └─ page.tsx
│  │  ├─ dashboard/
│  │  │  ├─ layout.tsx
│  │  │  ├─ page.tsx
│  │  │  ├─ loading.tsx
│  │  │  ├─ error.tsx
│  │  │  └─ _components/
│  │  └─ api/
│  │     └─ users/
│  │        └─ route.ts
│  ├─ components/
│  ├─ features/
│  ├─ lib/
│  ├─ hooks/
│  └─ types/
├─ .env.local
├─ next.config.js
├─ package.json
└─ tsconfig.json

En este ejemplo, src es solo un límite para el código de la aplicación, no un mecanismo obligatorio que modifique el comportamiento. Si un proyecto existente ya tiene app en la raíz, puede ser mejor seguir la convención establecida de forma coherente que forzar la incorporación de src. En particular, si existen directorios llamados app o pages tanto en la raíz como dentro de src, el directorio raíz tiene prioridad, por lo que es importante no mantener estructuras duplicadas durante un período prolongado al migrar. nextjs.org

El criterio práctico para elegir src es sencillo. Resulta útil si quieres separar claramente los archivos de configuración del código del producto o si esperas que aumente el número de archivos fuente. Por el contrario, no es necesario adoptarla en un proyecto de aprendizaje o en un proyecto muy pequeño cuya estructura raíz ya sea clara.

¿Qué archivos de la carpeta app crean rutas reales?

En App Router, las carpetas representan partes de una URL, o segmentos. Sin embargo, crear únicamente una carpeta app/dashboard no convierte /dashboard en una página pública. Si esa carpeta contiene page.tsx, se convierte en una ruta que proporciona UI de página; si contiene route.ts, se convierte en un endpoint de API basado en un Route Handler. nextjs.org

Por ejemplo, considera la siguiente estructura.

src/app/
├─ page.tsx
├─ about/
│  └─ page.tsx
├─ dashboard/
│  ├─ page.tsx
│  └─ reports/
│     └─ page.tsx
└─ api/
   └─ users/
      └─ route.ts

En este caso, page.tsx corresponde a /, /about, /dashboard y /dashboard/reports, respectivamente. api/users/route.ts no es una página de UI; define un endpoint de API. Una vez que comprendes que page.tsx y route.ts sirven como puntos de entrada públicos de una ruta, queda claro por qué otros archivos pueden residir en la misma carpeta. nextjs.org

Esta propiedad puede entenderse como colocación conjunta. La colocación conjunta es un enfoque organizativo que mantiene el código relacionado cerca. Por ejemplo, los componentes de tabla utilizados únicamente en dashboard, las funciones de formato específicas de pantalla y el código de transformación de datos de prueba pueden colocarse cerca de app/dashboard. Esto no significa que nunca deban utilizarse carpetas compartidas. Solo necesitas trasladar a carpetas con un alcance más amplio el código que se reutiliza en otras rutas.

¿Cómo deben separarse los archivos layout, loading y error?

layout.tsx gestiona el contenedor de UI compartido. El archivo raíz app/layout.tsx envuelve todas las rutas, mientras que un layout.tsx en una carpeta secundaria se aplica de manera anidada a sus subrutas. Por ejemplo, si la navegación del panel se comparte entre /dashboard y /dashboard/reports, puede residir en app/dashboard/layout.tsx. nextjs.org

page.tsx es la UI de página mostrada en una ruta específica. Si un layout es la estructura exterior que se repite, una página se aproxima más al contenido específico de la ruta que cambia dentro de ella. Separarlos implica que no necesitas repetir la navegación o los marcos compartidos en cada página.

App Router también proporciona archivos reservados para UI específica de estados. loading.tsx se utiliza para la UI de carga, error.tsx para la UI de errores y not-found.tsx para la UI de no encontrado. A diferencia de los archivos de componentes normales, Next.js interpreta estos archivos para funciones específicas, por lo que debes colocarlos teniendo en cuenta su función y alcance. nextjs.org

Por ejemplo, si quieres mostrar una pantalla independiente mientras se preparan datos en /dashboard, puedes considerar app/dashboard/loading.tsx; si necesitas una pantalla para gestionar errores dentro de ese alcance, puedes considerar app/dashboard/error.tsx. No es necesario añadir estos archivos mecánicamente a todas las carpetas. Decidir según si cada ruta necesita realmente UI independiente para los estados de espera, error o no encontrado mantiene la estructura concisa.

¿Cómo se representan las rutas dinámicas y las rutas de API en los nombres de carpeta?

Utiliza la notación de corchetes cuando una parte de una ruta no está predeterminada. [slug] significa un segmento dinámico, [...slug] significa todos los subsegmentos siguientes y [[...slug]] significa una forma opcional en la que esos subsegmentos pueden estar ausentes. nextjs.org

Por ejemplo, una página cuyo identificador de publicación cambia puede estructurarse de la siguiente manera.

src/app/posts/
└─ [slug]/
   └─ page.tsx

En esta estructura, [slug] no es un nombre de carpeta fijo; es un marcador de posición que recibe la parte cambiante de la URL. En cambio, cuando varios niveles de una ruta deben gestionarse mediante una única convención, considera [...slug] o [[...slug]]. La notación que debes elegir depende de si debe existir al menos una subruta y de si el caso sin ruta también debe gestionarse mediante la misma pantalla. nextjs.org

Al crear API, route.ts es el punto de entrada de un Route Handler. Por tanto, puedes representar la estructura de URL con carpetas como app/api/users/route.ts y colocar route.ts al final. Tanto para páginas como para API, leer la jerarquía de carpetas permite inferir la ruta aproximada. Sin embargo, es mejor separar adecuadamente los archivos de implementación dedicados para que la UI y el código de procesamiento del lado del servidor no queden excesivamente mezclados y extensos en la misma zona. nextjs.org

¿Por qué se necesitan los grupos de rutas y las carpetas privadas?

Una carpeta envuelta entre paréntesis, como (marketing), es un grupo de rutas. Es una agrupación lógica que no se incluye en la URL. Por ejemplo, puedes agrupar páginas orientadas al marketing, como información de la empresa y precios, mientras gestionas las pantallas de uso del producto en una estructura independiente. app/(marketing)/about/page.tsx se convierte en la ruta /about sin el nombre del grupo. nextjs.org

Los grupos de rutas son útiles cuando quieres mostrar límites de layout o propiedad del código sin cambiar la URL. Sin embargo, dado que el nombre del grupo no aparece en la URL, se produce un conflicto si grupos separados terminan creando la misma URL. Además, una configuración que se mueve entre varios layouts raíz puede causar una carga completa de página, por lo que dividir layouts raíz no debe aplicarse sin cuidado solo para que las carpetas parezcan ordenadas. nextjs.org

Las carpetas que comienzan con un guion bajo, como _components y _lib, son carpetas privadas y se excluyen del enrutamiento. En App Router, los archivos normales no se convierten en rutas de forma predeterminada, por lo que un guion bajo no es estrictamente necesario. Aun así, puede ser útil si quieres marcar visualmente el límite entre los archivos especiales de enrutamiento y la implementación interna, o evitar confusiones con nombres de archivos reservados. nextjs.org

Por ejemplo, app/dashboard/_components/summary-card.tsx comunica que se trata de UI específica del panel. Sin embargo, si ese componente empieza a utilizarse repetidamente en otras funcionalidades, es más natural considerar trasladarlo de la carpeta con guion bajo a components compartido o a un límite de funcionalidad adecuado. Recuerda que un prefijo de carpeta no es un mecanismo de control de acceso; es una notación que comunica el papel del código.

¿Cómo deberías distinguir components, features, lib, hooks y types?

Estas carpetas son opciones organizativas opcionales, no convenciones reservadas de App Router. Por tanto, los límites de responsabilidad acordados por el equipo importan más que los propios nombres. Las siguientes distinciones constituyen un punto de partida habitual.

CarpetaCódigo que se coloca habitualmenteCriterio de ubicación
componentsUI reutilizada en varias pantallas¿No está ligada a una URL o dominio específico?
featuresImplementación a nivel de funcionalidad o dominio¿Existe un concepto de negocio claro, como cuentas, pedidos o paneles?
libUtilidades y clientes compartidos¿Es una herramienta compartida en lugar de UI?
hooksHooks reutilizables¿Varios componentes comparten el mismo estado o comportamiento?
typesTipos compartidos¿Varias áreas hacen referencia a las mismas definiciones de tipos?

components puede contener no solo botones genéricos, sino también UI compuesta compartida entre varias funcionalidades. Sin embargo, convertir desde el principio cada elemento de UI en un componente compartido globalmente puede abstraer implementaciones que en realidad solo necesita una pantalla. Otro enfoque consiste en mantenerlo cerca de la ruta inicialmente y trasladarlo cuando se utilice de forma estable en dos o más lugares y tenga una interfaz compartida clara.

features resulta especialmente fácil de leer cuando se necesita una estructura centrada en el dominio. Por ejemplo, si los pedidos y las cuentas tienen cada uno pantallas, UI y código de procesamiento de datos independientes, puedes agruparlos como features/orders y features/account. Por el contrario, crear demasiadas carpetas de funcionalidades en un sitio simple puede obligar a las personas a recorrer muchas carpetas solo para encontrar archivos. Es mejor introducirlas únicamente cuando los límites de las funcionalidades se alineen con conceptos reales del producto.

lib es una ubicación candidata para código base no relacionado con UI, como utilidades compartidas o clientes de servidor. Sin embargo, si todos los archivos de funciones se acumulan en lib, puede convertirse en un gran espacio de almacenamiento difícil de entender. Una regla práctica es mantener cerca de esa funcionalidad o ruta las herramientas utilizadas solo por una funcionalidad, y trasladar a lib únicamente el código compartido en varios lugares.

¿Por qué public y los archivos de variables de entorno pertenecen a la raíz del proyecto?

public es la carpeta de la raíz del proyecto para archivos estáticos. Los archivos colocados allí se sirven desde la ruta raíz; por ejemplo, se hace referencia a public/profile.png como /profile.png. Esto proporciona un único lugar para los archivos que se sirven estáticamente, como imágenes y fuentes. nextjs.org

Incluso al usar src, no deberías entender la estructura como trasladar public a src/public. public permanece en la raíz del proyecto, y package.json, next.config.js, tsconfig.json y .env.* también se gestionan desde la raíz. En particular, dado que los archivos de variables de entorno locales como .env.local pueden contener secretos, es importante establecer una regla operativa para no incluirlos en el control de versiones. nextjs.org

Distinguir las funciones de los recursos estáticos y del código fuente de la aplicación facilita interpretar las rutas. Los archivos de src/app son código que crea pantallas o enrutamiento, mientras que los archivos de public son recursos estáticos a los que se hace referencia por URL. Incluso cuando se utiliza la misma imagen en una pantalla, debes comprender de forma distinta su método de entrega y su ruta de referencia según dónde se coloque el archivo.

¿En qué se diferencia esto de la estructura de Pages Router?

Pages Router funciona tratando los archivos del directorio pages como rutas. Por ejemplo, pages/index.tsx corresponde a / y pages/about.tsx corresponde a /about. Las convenciones de archivos reservados también asignan funciones especiales a _app, _document, 404, 500 y otros. nextjs.org

Es fácil cometer errores si consideras App Router y Pages Router como enfoques que difieren únicamente en el nombre de la carpeta. En App Router, archivos especiales como page.tsx, layout.tsx y route.ts bajo los segmentos de carpeta dividen las responsabilidades, mientras que los archivos normales no son rutas de forma predeterminada. En Pages Router, los archivos dentro de pages están conectados más directamente con las rutas. nextjs.orgnextjs.org

Al trabajar con un proyecto existente de Pages Router, debes respetar sus convenciones actuales basadas en pages. Por el contrario, al iniciar un proyecto nuevo, basar la estructura en un ejemplo centrado en App Router y añadir carpetas organizativas solo cuando sean realmente necesarias reduce la sobrecarga. Es importante no confundir las convenciones de los dos routers dentro del mismo diseño de directorios.

¿Qué criterios deberías utilizar para elegir una estructura en un proyecto real?

Primero, empieza por la estructura de URL. Enumera las rutas principales a las que accederán los usuarios y, a continuación, determina qué layouts compartidos utiliza cada ruta. Representa el resultado mediante las carpetas de app y la ubicación de page.tsx y layout.tsx. Utiliza grupos de rutas cuando exista una razón clara para dividir áreas de pantalla o layouts sin cambiar las URL. nextjs.orgnextjs.org

Segundo, evalúa el alcance de la reutilización. Mantén cerca de la ruta el código utilizado por una sola ruta. La UI utilizada por varias rutas puede pasar a un alcance más amplio como components, y las utilidades utilizadas por varias funcionalidades pueden pasar a lib. En lugar de generalizar todo desde el inicio, separar el código cuando surjan patrones reales de reutilización y cambio ayuda a reducir la abstracción innecesaria.

Tercero, considera la independencia de las funcionalidades. Si crecen funcionalidades con áreas de responsabilidad y terminología claras, como cuentas, administración o pedidos, los límites de dominio como features pueden ser útiles. Por otro lado, cuando hay pocas pantallas y las distinciones entre funcionalidades son débiles, la colocación conjunta en app más un número reducido de carpetas compartidas puede ser suficiente.

Cuarto, comprueba el coste de descubrimiento para el equipo. Cuando un miembro nuevo del equipo busca el código de una URL concreta, debería poder seguir la ruta de app y encontrar la página y la implementación dedicada. Los nombres y ubicaciones de botones o utilidades compartidos también deberían ser previsibles. Una buena estructura surge de esta previsibilidad más que de nombres de carpetas de moda.

¿Qué ideas erróneas y problemas estructurales deberías evitar?

La primera idea errónea es que «crear una carpeta crea inmediatamente una URL». En App Router, una carpeta representa un segmento, pero necesita page.tsx o route.ts para convertirse en una página pública o endpoint de API. Esta regla permite que los archivos internos relacionados residan juntos en la carpeta de la ruta. nextjs.org

La segunda idea errónea es que «sin una carpeta con guion bajo, todos los archivos internos quedan expuestos». Los archivos normales en App Router no son rutas de forma predeterminada. Un nombre como _components no es una función de seguridad obligatoria; es una herramienta organizativa que indica implementación interna y excluye la carpeta del enrutamiento. nextjs.org

La tercera idea errónea es que «los nombres de los grupos de rutas también se incluyen en las URL». El nombre entre paréntesis de (marketing) se excluye de la URL. Debido a esta comodidad, debes asegurarte de que distintos grupos no creen la misma URL final. Si divides varios layouts raíz, la posibilidad de una carga completa de página al navegar entre grupos es otro aspecto que debes considerar antes de diseñar la estructura. nextjs.org

Por último, evita el error de mantener directorios app o pages equivalentes tanto en la raíz como en src mientras introduces src. Dado que la raíz tiene prioridad en este caso, puede parecer que no se está ejecutando el código fuente que esperabas. Cambiar una estructura no consiste simplemente en añadir carpetas de una vez; avanza verificando qué directorio sirve realmente como base para el enrutamiento. nextjs.org

Conclusión: el estándar se refiere a los límites de responsabilidad, no a una lista de carpetas

El punto de partida para la estructura de un proyecto Next.js son las convenciones de enrutamiento definidas por app y los archivos especiales. En un proyecto nuevo con App Router, puedes usar src/app como centro para URL, páginas y layouts; mantener public como carpeta de recursos estáticos en la raíz; y elegir components, features, lib, hooks y types según el alcance real de reutilización del código y la complejidad del dominio. nextjs.orgnextjs.org

En última instancia, una buena estructura no es la que tiene más carpetas. Es aquella en la que los miembros del equipo pueden predecir fácilmente la ubicación de una pantalla para una ruta concreta, el código dedicado a esa pantalla y el código compartido en varios lugares. Sigue con precisión las convenciones de archivos de Next.js y, después, ajusta de forma incremental el enfoque organizativo sobre ellas a medida que evolucionen la tasa de crecimiento y los patrones de cambio del proyecto.

Preguntas frecuentes

¿Es obligatorio usar una carpeta src en Next.js?

No. src es opcional. Si la utilizas, puedes mantener app y el código de la aplicación dentro de src, pero public, los principales archivos de configuración y los archivos de variables de entorno permanecen en la raíz del proyecto. Si existen directorios app o pages equivalentes tanto en la raíz como en src, el directorio raíz tiene prioridad.

¿Cada carpeta creada dentro de la carpeta app se convierte en una URL?

No. Las carpetas representan segmentos de URL, pero un segmento necesita page.tsx o route.ts para convertirse en una página o endpoint de API accesible públicamente. Esto permite que los componentes y utilidades específicos de una ruta residan dentro de la carpeta de esa ruta.

¿Las carpetas entre paréntesis como (marketing) se incluyen en la URL?

No. Los nombres entre paréntesis son grupos de rutas, que organizan las rutas de forma lógica o aplican layouts independientes sin cambiar la URL. Sin embargo, se produce un conflicto si grupos diferentes generan la misma URL.

¿Un proyecto nuevo de Next.js debería usar App Router o Pages Router?

Pages Router sigue siendo compatible, pero la documentación oficial recomienda migrar a App Router para utilizar las funciones más recientes de React. Salvo que existan restricciones específicas de una estructura ya existente, un proyecto nuevo puede considerar App Router como la opción predeterminada; un proyecto existente con Pages Router puede seguir sus convenciones de enrutamiento actuales o evaluar una migración según sea necesario.

¿Cómo se hace referencia a archivos de la carpeta public?

public es la carpeta de archivos estáticos ubicada en la raíz del proyecto. Por ejemplo, un archivo situado en public/profile.png se referencia como /profile.png.