Un portafolio de desarrollador es la señal más directa de lo que eres capaz de construir. Los reclutadores dedican una media de 6 segundos a un portafolio antes de decidir si profundizan o pasan de largo. Eso significa que cada sección tiene que justificar su sitio: jerarquía clara, tiempos de carga rápidos y cero relleno. Esta guía recorre la creación de un portafolio de desarrollador completo en React con secciones listas para usar, desde el hero hasta el formulario de contacto.
El conjunto de secciones
Un portafolio de desarrollador que convierte sigue una estructura predecible. Cada sección tiene una única función:
- Hero: quién eres, a qué te dedicas, un solo CTA
- Vitrina de proyectos: tus 3 a 6 mejores proyectos en una cuadrícula o layout bento
- Sobre mí / Skills: trayectoria, stack tecnológico, personalidad
- Timeline: experiencia laboral y/o formación, en orden cronológico inverso
- Contacto: formulario o enlaces directos, sin fricción
Son cinco secciones. Ni diez ni dos. Con cinco secciones cubres todo lo que un responsable de contratación o un cliente potencial necesita ver. Vamos a construir cada una.
1. Hero con nombre y título
El hero de un portafolio es más sencillo que el de un SaaS. Sin barra de prueba social, sin CTA secundario, sin badge. Solo tu nombre, tu título y una única llamada a la acción:
interface PortfolioHeroProps {
name: string;
title: string;
description: string;
ctaLabel: string;
ctaUrl: string;
avatarUrl?: string;
}
export default function PortfolioHero({ name, title, description, ctaLabel, ctaUrl, avatarUrl }: PortfolioHeroProps) {
return (
<section
style={{
minHeight: "90vh",
display: "flex",
alignItems: "center",
padding: "var(--section-padding-y-lg) 0",
background: "var(--color-background)",
}}
>
<div style={{
maxWidth: "var(--container-max-width)",
margin: "0 auto",
padding: "0 var(--container-padding-x)",
display: "flex",
flexDirection: "column",
alignItems: "center",
textAlign: "center",
gap: "1.5rem",
}}>
{avatarUrl && (
<img
src={avatarUrl}
alt={name}
style={{ width: 96, height: 96, borderRadius: "50%", objectFit: "cover" }}
/>
)}
<h1 style={{
fontSize: "clamp(2.5rem, 5vw, 4rem)",
fontWeight: 700,
lineHeight: 1.1,
letterSpacing: "-0.03em",
color: "var(--color-foreground)",
}}>
{name}
</h1>
<p style={{
fontSize: "1.25rem",
color: "var(--color-accent)",
fontWeight: 500,
}}>
{title}
</p>
<p style={{
fontSize: "1.0625rem",
lineHeight: 1.7,
color: "var(--color-foreground-muted)",
maxWidth: "560px",
}}>
{description}
</p>
<a
href={ctaUrl}
style={{
display: "inline-flex",
alignItems: "center",
gap: "8px",
padding: "0.875rem 2rem",
borderRadius: "var(--radius-full)",
background: "var(--color-accent)",
color: "#fff",
fontWeight: 600,
fontSize: "0.9375rem",
textDecoration: "none",
}}
>
{ctaLabel}
</a>
</div>
</section>
);
}
El CTA debería enlazar a tu sección de proyectos (#projects) o directamente a tu formulario de contacto (#contact), según tu objetivo. Si estás buscando trabajo, un "Ver mi trabajo" que apunte a los proyectos es la opción más potente.
2. Cuadrícula de la vitrina de proyectos
La cuadrícula de proyectos es la sección más importante de tu portafolio. Usa un CSS Grid responsive con 2 columnas en escritorio y 1 en móvil:
interface Project {
title: string;
description: string;
tags: string[];
imageUrl: string;
liveUrl?: string;
repoUrl?: string;
}
export function ProjectGrid({ projects }: { projects: Project[] }) {
return (
<section id="projects" style={{ padding: "var(--section-padding-y) 0" }}>
<div style={{
maxWidth: "var(--container-max-width)",
margin: "0 auto",
padding: "0 var(--container-padding-x)",
}}>
<h2 style={{ fontSize: "2rem", fontWeight: 700, marginBottom: "3rem" }}>
Selected Work
</h2>
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 400px), 1fr))",
gap: "2rem",
}}>
{projects.map((project) => (
<article
key={project.title}
style={{
borderRadius: "var(--radius-lg)",
border: "1px solid var(--color-border)",
overflow: "hidden",
background: "var(--color-surface)",
}}
>
<img
src={project.imageUrl}
alt={project.title}
style={{ width: "100%", height: 240, objectFit: "cover" }}
/>
<div style={{ padding: "1.5rem" }}>
<h3 style={{ fontSize: "1.25rem", fontWeight: 600 }}>{project.title}</h3>
<p style={{
fontSize: "0.9375rem",
color: "var(--color-foreground-muted)",
lineHeight: 1.6,
marginTop: "0.5rem",
}}>
{project.description}
</p>
<div style={{ display: "flex", flexWrap: "wrap", gap: "0.5rem", marginTop: "1rem" }}>
{project.tags.map((tag) => (
<span
key={tag}
style={{
padding: "0.25rem 0.75rem",
borderRadius: "var(--radius-full)",
background: "var(--color-accent-subtle)",
color: "var(--color-foreground-muted)",
fontSize: "0.8125rem",
}}
>
{tag}
</span>
))}
</div>
</div>
</article>
))}
</div>
</div>
</section>
);
}
Selecciona sin piedad. Tres proyectos excelentes ganan a diez mediocres. Cada tarjeta de proyecto debería incluir: una captura de pantalla (no un logo), una descripción de una frase sobre qué construiste y por qué, y las tech tags. Enlaza al sitio en producción siempre que puedas: los reclutadores quieren hacer clic y verlo funcionando.
3. Sección Sobre mí y Skills
La sección sobre mí te humaniza más allá de una lista de proyectos. Mantenla concisa, dos párrafos cortos como máximo, y acompáñala de una cuadrícula de skills:
<div style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(120px, 1fr))",
gap: "1rem",
marginTop: "2rem",
}}>
{skills.map((skill) => (
<div
key={skill.name}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "0.5rem",
padding: "1.25rem",
borderRadius: "var(--radius-md)",
border: "1px solid var(--color-border)",
}}
>
{skill.icon}
<span style={{ fontSize: "0.8125rem", fontWeight: 500 }}>{skill.name}</span>
</div>
))}
</div>
Muestra entre 8 y 12 skills como máximo. Si listas 30 tecnologías, ninguna parece un punto fuerte. Agrúpalas de forma lógica: lenguajes, frameworks, herramientas. Usa iconos reconocibles (de lucide-react o react-icons) en lugar de texto plano, porque el ojo escanea más rápido lo visual.
4. Sección Timeline
Un timeline cronológico inverso de tu experiencia laboral le da a los reclutadores los datos estructurados que están buscando. El patrón visual es una línea vertical con nodos:
interface TimelineItem {
date: string;
title: string;
company: string;
description: string;
}
export function Timeline({ items }: { items: TimelineItem[] }) {
return (
<div style={{ position: "relative", paddingLeft: "2rem" }}>
{/* Vertical line */}
<div
aria-hidden
style={{
position: "absolute",
left: "7px",
top: 0,
bottom: 0,
width: "2px",
background: "var(--color-border)",
}}
/>
{items.map((item) => (
<div key={`${item.company}-${item.date}`} style={{ position: "relative", paddingBottom: "2.5rem" }}>
{/* Node dot */}
<div
aria-hidden
style={{
position: "absolute",
left: "-2rem",
top: "4px",
width: "16px",
height: "16px",
borderRadius: "50%",
background: "var(--color-accent)",
border: "3px solid var(--color-background)",
}}
/>
<span style={{ fontSize: "0.8125rem", color: "var(--color-foreground-muted)" }}>
{item.date}
</span>
<h3 style={{ fontSize: "1.125rem", fontWeight: 600, marginTop: "0.25rem" }}>
{item.title}
</h3>
<p style={{ fontSize: "0.9375rem", color: "var(--color-accent)", fontWeight: 500 }}>
{item.company}
</p>
<p style={{
fontSize: "0.9375rem",
color: "var(--color-foreground-muted)",
lineHeight: 1.6,
marginTop: "0.5rem",
}}>
{item.description}
</p>
</div>
))}
</div>
);
}
Limita cada descripción a 1 o 2 frases. Pon el foco en el impacto ("Reduje el tiempo de carga un 40 %") por encima de las responsabilidades ("Responsable del desarrollo frontend").
5. Formulario de contacto
La última sección elimina la fricción. Un formulario sencillo con campos de nombre, email y mensaje es suficiente. No pidas número de teléfono, tamaño de empresa ni presupuesto, porque esto no es un formulario de captación de leads de un SaaS:
<form
action="/api/contact"
method="POST"
style={{ display: "flex", flexDirection: "column", gap: "1rem", maxWidth: "480px" }}
>
<input
name="name"
placeholder="Name"
required
style={{
padding: "0.75rem 1rem",
borderRadius: "var(--radius-md)",
border: "1px solid var(--color-border)",
background: "var(--color-surface)",
color: "var(--color-foreground)",
fontSize: "0.9375rem",
}}
/>
<input name="email" type="email" placeholder="Email" required style={{ /* same styles */ }} />
<textarea
name="message"
placeholder="Message"
rows={5}
required
style={{ /* same styles, resize: "vertical" */ }}
/>
<button
type="submit"
style={{
padding: "0.875rem 2rem",
borderRadius: "var(--radius-full)",
background: "var(--color-accent)",
color: "#fff",
fontWeight: 600,
border: "none",
cursor: "pointer",
}}
>
Send Message
</button>
</form>
Añade enlaces sociales (GitHub, LinkedIn, Twitter/X) junto al formulario o debajo. Hay visitantes que prefieren escribirte por DM antes que rellenar un formulario.
Montar el portafolio completo
Cada sección anterior es independiente y autónoma. Colócalas dentro de un único componente de página en este orden: Hero, Proyectos, Sobre mí, Timeline, Contacto. Añade una navbar fija con enlaces de ancla (#projects, #about, #experience, #contact) y un footer, y ya tienes un portafolio completo.
En lugar de construir cada sección desde cero, echa un vistazo al catálogo de heros de Incubator para variantes de hero pulidas, al catálogo de portafolios para layouts de vitrina de proyectos, al catálogo de secciones sobre mí para secciones de bio y skills, y al catálogo de contacto para diseños de formulario. Si quieres un portafolio completo montado a partir de secciones listas para usar, el generador de páginas de portafolio te permite elegir secciones y exportar una página entera. Cada sección viene preparada para TypeScript, Tailwind CSS v4 y Next.js 15: pega, personaliza tu contenido y despliega.
Relacionado en incubator
- Catálogo de secciones de portafolio: layouts de vitrina de proyectos listos para React.
- Componentes de timeline: secciones de experiencia e historial laboral en modo copy-paste.
- Catálogo de secciones hero: variantes de hero pulidas para la landing de tu portafolio.
- Catálogo de secciones sobre mí: secciones de bio, skills y equipo listas para usar.
- Efectos de animación al hacer scroll: secciones con animaciones de entrada activadas por el scroll.