Tutorial: Primeros pasos con Nextjs — 5. Creando la estructura de carpetas
- 1. Creando el proyecto en Content Island
- 2. Definiendo el modelo de datos
- 3. Añadiendo datos
- 4. Creando el proyecto en Nextjs
- 5. Creando la estructura de carpetas
- 6. Conectando Content Island con Nextjs
- 7. Montando la página de About
- 8. Página con lista de posts
- 9. Montando un post
- 10. Desplegando en Vercel
5. Creando la estructura de carpetas
Vamos ahora con la creación de la estructura de carpetas. En nuestro sitio web, queremos tener las siguientes páginas:
- Home: mostrará un listado de posts.
- About: incluirá información sobre el autor.
- Post: presentará detalles sobre un post en concreto.
De momento nos centramos en Home y About.
Para la página Home, vamos a usar el archivo principal que ya existe en : ./src/app/page.tsx
Para la de About, vamos a crear una subcarpeta (más adelante habrá más ficheros relacionados)
./src/app/about/page.tsx
export default function About() {
return (
<h1>About</h1>
);
}
Ya que estamos, vamos a añadir una hoja de estilo global:
- Definiremos unas variables de color y tipografía.
- Definiremos unos estilos base.
Reemplazamos el contenido existente por el siguiente. Si estás montando el ejemplo desde cero, puedes copiarlo, desde la guía de get started del portal de Content Island, o del enlace del ejemplo en GitHub.
./src/app/globals.css
:root {
--primary-color: #1d3353;
--pure-white: #fff;
/*text colors*/
--text-color: #333;
--text-color-light: var(--pure-white);
/*hover colors*/
--title-hover-color: hsl(
from var(--primary-color) h calc(s + 20) calc(l + 10)
);
--menu-hover-color: hsl(
from var(--primary-color) h calc(s + 20) calc(l + 63)
);
/*border color*/
--border-color: #333;
/*font family*/
--ff-title: "IBM Plex Sans Condensed", sans-serif;
--ff-body: "IBM Plex Sans", sans-serif;
/*font sizes*/
--fs-2xs: clamp(0.7813rem, 0.7747rem + 0.0326vw, 0.8rem);
--fs-xs: clamp(0.9375rem, 0.9158rem + 0.1087vw, 1rem);
--fs-sm: clamp(1.125rem, 1.0815rem + 0.2174vw, 1.25rem);
--fs-md: clamp(1.35rem, 1.2761rem + 0.3696vw, 1.5625rem);
--fs-lg: clamp(1.62rem, 1.5041rem + 0.5793vw, 1.9531rem);
--fs-xl: clamp(1.944rem, 1.771rem + 0.8651vw, 2.4414rem);
--fs-2xl: clamp(2.3328rem, 2.0827rem + 1.2504vw, 3.0518rem);
--fs-3xl: clamp(2.7994rem, 2.4462rem + 1.7658vw, 3.8147rem);
--fs-4xl: clamp(3.3592rem, 2.8691rem + 2.4507vw, 4.7684rem);
/*spacing*/
--space-3xs: clamp(0.25rem, 0.5vw, 0.5rem);
--space-2xs: clamp(0.5rem, 1vw, 1rem);
--space-xs: clamp(1rem, 2vw, 1.5rem);
--space-sm: clamp(1.5rem, 3vw, 2rem);
--space-md: clamp(2rem, 4vw, 3rem);
--space-lg: clamp(3rem, 6vw, 4rem);
--space-xl: clamp(4rem, 8vw, 6rem);
--space-2xl: clamp(6rem, 10vw, 8rem);
/*border radius*/
--border-radius: 6px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--ff-body);
font-size: var(--fs-sm);
display: flex;
flex-direction: column;
min-height: 100vh;
color: var(--text-color);
}
main {
flex: 1;
}
.container {
width: 90%;
max-width: 1200px;
margin-right: auto;
margin-left: auto;
padding-top: var(--space-xl);
padding-bottom: var(--space-xl);
position: relative;
}Ahora vamos a crear un par de componentes, uno para crear un header para tener el menú de nuestro blog.
./src/components/header.module.css
.header {
background-color: var(--primary-color);
color: var(--text-color-light);
padding: var(--fs-xs);
text-align: center;
position: sticky;
top: 0;
z-index: 1;
}
.menu ul {
display: flex;
justify-content: space-between;
list-style: none;
}
.menu__item {
color: var(--text-color-light);
text-decoration: none;
transition: color 0.3s ease-in-out;
}
.menu__item:hover,
.menu__item:active,
.menu__item:focus {
color: var(--menu-hover-color);
}
.menu__right {
display: flex;
gap: var(--space-md);
}
./src/components/header.component.tsx
import classes from './header.module.css';
export const Header = () => {
return (
<header className={classes.header}>
<nav className={classes.menu}>
<ul>
<li>
<a href="/" className={classes.menu__item}>
CurioVerso
</a>
</li>
<div className={classes.menu__right}>
<li>
<a href="/" className={classes.menu__item}>
All Post
</a>
</li>
<li>
<a href="/about/" className={classes.menu__item}>
About
</a>
</li>
</div>
</ul>
</nav>
</header>
);
};
Vamos a añadir un segundo componente, un footer:
./src/components/footer.module.css
.footer {
background-color: var(--text-color);
color: var(--text-color-light);
padding: var(--fs-xs);
text-align: center;
}
./src/components/footer.component.tsx
import classes from './footer.module.css';
export const Footer = () => {
return (
<footer className={classes.footer}>
<p>© 2025</p>
</footer>
);
};
Y vamos a modificar el layout principal para que utilice el header y el footer (sustituimos el contenido por completo).
./src/app/layout.tsx
import type { Metadata } from "next";
import { IBM_Plex_Sans_Condensed, IBM_Plex_Sans } from "next/font/google";
import { Header } from "../components/header.component";
import { Footer } from "../components/footer.component";
import "./globals.css";
const condensed = IBM_Plex_Sans_Condensed({
subsets: ["latin"],
weight: ["100", "200", "300", "400", "500", "600", "700"],
style: ["normal", "italic"],
});
const sans = IBM_Plex_Sans({
subsets: ["latin"],
weight: ["100", "200", "300", "400", "500", "600", "700"],
style: ["normal", "italic"],
});
export const metadata: Metadata = {
title: "Content Island with Next.js",
description: "Creating a blog with Content Island and Next.js",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en" className={`${condensed.className} ${sans.className}`}>
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
Ahora sí, arrancamos el proyecto con npm run dev, vemos la página principal y podemos navegar a la de About desde el menú.