Cómo construí mi portafolio con Next.js y Vercel
Un análisis profundo de la creación de un portafolio de desarrollador moderno, rápido y optimizado para SEO con Next.js 13, TypeScript, Tailwind CSS y Vercel. Detallo decisiones de arquitectura, optimizaciones y buenas prácticas.
Introducción
Como desarrollador full-stack, tener un portafolio profesional es esencial para mostrar tus habilidades a reclutadores y potenciales clientes. Después de evaluar varios frameworks y enfoques, elegí construir mi portafolio con Next.js 13, TypeScript y Tailwind CSS, y desplegarlo en Vercel.
En este artículo, te explico las decisiones clave, la arquitectura y las optimizaciones que hacen que este portafolio destaque. Ya sea que quieras crear tu propio portafolio o simplemente conocer prácticas modernas de desarrollo web, esta guía te dará información valiosa.
¿Por qué Next.js?
Next.js se convirtió en mi framework principal para aplicaciones React, y estas son las razones por las que es perfecto para un portafolio:
Renderizado del lado del servidor y generación estática
Next.js ofrece varias estrategias de renderizado listas para usar:
// Static Site Generation (SSG) - Ideal para portafolios
export async function generateStaticParams() {
const posts = getAllPostSlugs();
return posts.map((slug) => ({ slug }));
}
// Esta página se genera en build
export default function BlogPost({ params }: { params: { slug: string } }) {
const post = getPostBySlug(params.slug);
return <Article post={post} />;
}
Para un portafolio, la generación estática (SSG) es ideal porque:
- El contenido no cambia con frecuencia
- Las páginas cargan al instante (HTML pre-renderizado)
- Excelente SEO (los motores pueden rastrear el contenido)
- Costo de servidor cero (archivos estáticos)
Arquitectura App Router
Next.js 13 introdujo el App Router, que utilicé para una organización clara:
app/
+-- layout.tsx # Layout raíz con providers
+-- page.tsx # Página de inicio
+-- blog/
¦ +-- page.tsx # Listado del blog
¦ +-- [slug]/
¦ +-- page.tsx # Artículo individual
+-- globals.css
Este enrutamiento basado en archivos hace que la base de código sea intuitiva y mantenible.
TypeScript para seguridad de tipos
TypeScript no solo ayuda a evitar errores: también documenta el código y mejora el tooling:
// Definiciones de tipos para artículos
export type BlogPostFrontmatter = {
title: string;
description: string;
author: string;
date: string;
tags: string[];
coverImage: string;
published: boolean;
seoTitle?: string;
seoDescription?: string;
};
export type BlogPost = {
slug: string;
frontmatter: BlogPostFrontmatter;
content: string;
readingTime: {
text: string;
minutes: number;
words: number;
};
};
Con estos tipos, mi IDE ofrece autocompletado, detecta errores y hace que el refactor sea seguro.
Estilos con Tailwind CSS
Elegí Tailwind CSS por su enfoque utility-first y su excelente experiencia de desarrollo:
// Componente limpio y legible con Tailwind
function ProjectCard({ project }: { project: Project }) {
return (
<div className="group relative overflow-hidden rounded-xl border border-border bg-card transition-all hover:shadow-lg">
<div className="aspect-video relative">
<Image
src={project.image}
alt={project.title}
fill
className="object-cover transition-transform group-hover:scale-105"
/>
</div>
<div className="p-6">
<h3 className="text-xl font-bold text-foreground mb-2">
{project.title}
</h3>
<p className="text-muted-foreground line-clamp-2">
{project.description}
</p>
</div>
</div>
);
}
Variables CSS para el tema
Uso variables CSS para un sistema claro/oscuro robusto:
:root {
--background: 0 0% 98%;
--foreground: 215 25% 15%;
--primary: 0 84% 60%;
--muted: 215 20% 95%;
}
.dark {
--background: 224 27% 6%;
--foreground: 210 40% 95%;
--primary: 0 90% 65%;
--muted: 224 27% 12%;
}
Este enfoque permite un cambio de tema fluido sin lógica compleja.
Optimizaciones de rendimiento
El rendimiento es clave para la experiencia de usuario y el SEO. Esto es lo que implementé:
Optimización de imágenes
El componente Image de Next.js se encarga de optimizar automáticamente:
import Image from 'next/image';
<Image
src={post.coverImage}
alt={post.title}
fill
priority // Cargar imágenes above-the-fold inmediatamente
sizes="(max-width: 768px) 100vw, 60vw"
className="object-cover"
/>
División de código
Next.js divide el código por ruta, y además uso imports dinámicos:
import dynamic from 'next/dynamic';
// Cargar solo cuando sea necesario
const ProjectsSection = dynamic(
() => import('@/components/sections/projects'),
{ loading: () => <ProjectsSkeleton /> }
);
Optimización de fuentes
next/font elimina el layout shift y optimiza la carga:
import { Syne } from 'next/font/google';
const syne = Syne({
subsets: ['latin'],
weight: ['400', '500', '600', '700', '800'],
display: 'swap',
});
Implementación SEO
Para un portafolio, el SEO es crítico. Este es mi enfoque:
Metadatos dinámicos
export async function generateMetadata({ params }): Promise<Metadata> {
const post = getPostBySlug(params.slug);
return {
title: `${post.frontmatter.title} | Mouhssine Lakhili`,
description: post.frontmatter.description,
openGraph: {
title: post.frontmatter.title,
description: post.frontmatter.description,
type: 'article',
publishedTime: post.frontmatter.date,
authors: [post.frontmatter.author],
},
twitter: {
card: 'summary_large_image',
title: post.frontmatter.title,
description: post.frontmatter.description,
},
};
}
Datos estructurados (JSON-LD)
Agrego schema markup para resultados enriquecidos:
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.frontmatter.title,
description: post.frontmatter.description,
author: {
'@type': 'Person',
name: 'Mouhssine Lakhili',
},
datePublished: post.frontmatter.date,
image: post.frontmatter.coverImage,
};
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
Generación automática del sitemap
Un script pre-build genera el sitemap:
// scripts/generate-sitemap.js
function generateSitemap() {
const blogPosts = getBlogPosts();
const urls = [
{ loc: SITE_URL, priority: '1.0' },
{ loc: `${SITE_URL}/blog`, priority: '0.8' },
...blogPosts.map(post => ({
loc: `${SITE_URL}/blog/${post.slug}`,
priority: '0.7',
lastmod: post.date,
})),
];
// Generate XML...
}
Internacionalización (i18n)
Mi portafolio soporta inglés, francés y español:
const translations = {
en: {
'nav.home': 'Home',
'nav.about': 'About',
'nav.projects': 'Projects',
// ...
},
fr: {
'nav.home': 'Accueil',
'nav.about': 'A propos',
'nav.projects': 'Projets',
// ...
},
es: {
'nav.home': 'Inicio',
'nav.about': 'Acerca de',
'nav.projects': 'Proyectos',
// ...
},
};
Gracias a un contexto de React, el cambio de idioma es fluido y se mantiene entre sesiones.
Despliegue en Vercel
Vercel ofrece la mejor experiencia para despliegues con Next.js:
- Cero configuración: conectar el repo de GitHub
- Previsualizaciones: cada PR tiene su propia URL
- Edge network: contenido servido desde 100+ ubicaciones
- Analytics: monitoreo de rendimiento integrado
# Eso es todo lo que necesitas
vercel deploy
Puntos clave
Construir este portafolio me dejó varias lecciones importantes:
- Empezar con rendimiento en mente: es más fácil mantenerlo rápido que acelerar algo lento
- Tipar todo: TypeScript detecta bugs antes de llegar al usuario
- SEO desde el día uno: estructura el contenido temprano
- Accesibilidad: HTML semántico y ARIA
- Mantenerlo simple: evitar la sobre-ingeniería
¿Qué sigue?
Sigo mejorando este portafolio. Próximas mejoras:
- RSS para el blog
- Seguimiento de vistas
- Integración de newsletter
- Más componentes interactivos
Conclusión
Construir un portafolio es más que mostrar proyectos: es una oportunidad para demostrar tus habilidades en acción. Cada línea de código, cada decisión de diseño y cada optimización hablan de tus capacidades.
Si estás pensando en crear tu propio portafolio, te recomiendo empezar con Next.js. La experiencia de desarrollo es excelente, el rendimiento es sobresaliente y el ecosistema es sólido.
Puedes explorar mi GitHub, mi perfil o mi pagina freelance si tienes preguntas.
¿Quieres hablar sobre este artículo o tienes preguntas sobre cómo construir tu propio portafolio? Conectemos en LinkedIn o Twitter.
Articulos relacionados
- Como funcionan los agentes de IA - entender la arquitectura detras de los sistemas de agentes.
- Model Context Protocol explicado - ver como conectar herramientas y contexto en flujos de agentes.
Construir con IA y entregar bien
Necesitas un desarrollador capaz de llevar una idea a produccion?
Ayudo a equipos a entregar trabajo en React, Next.js, Node.js, IA y automatizacion con alcance claro, guardrails utiles y ejecucion rapida.
Articulos relacionados
Cómo funcionan realmente los agentes de IA: arquitectura, memoria, herramientas y el bucle del agente
Guía técnica sobre la arquitectura de un agente de IA: bucle del agente, herramientas, memoria (RAG/vector DB), evaluación y fallos comunes en producción.
Por qué fallan los agentes de IA (y cómo arreglarlos)
Guía práctica sobre fallas de agentes IA en producción y cómo corregirlas con objetivos claros, memoria, herramientas, evaluación, UX y seguridad.
Cómo Construir Agentes IA con LangChain: Tutorial Completo 2026
Tutorial paso a paso para construir agentes IA listos para producción con LangChain. Desde setup hasta despliegue con herramientas, memoria, evaluación y manejo de errores.
