Variaciones de sintaxis de componentes de función
Todas las formas de escribir un componente de función de React - declaración vs. expresión, arrow vs. function, export por defecto vs. nombrado, y cuándo importa cada forma.
Busca en todas las páginas de la documentación
Todas las formas de escribir un componente de función de React - declaración vs. expresión, arrow vs. function, export por defecto vs. nombrado, y cuándo importa cada forma.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Tarjeta de referencia rápida.
// 1. Declaración de función (la más común)
function Greeting({ name }: { name: string }) {
return <h1>Hola, {name}</h1>;
}
// 2. Arrow function asignada a un const
const Greeting = ({ name }: { name: string }) => {
return <h1>Hola, {name}</h1>;
};
// 3. Arrow function con retorno implícito
const Greeting = ({ name }: { name: string }) => (
<h1>Hola, {name}</h1>
);
// 4. Export por defecto - declaración de función
export default function Greeting({ name }: { name: string }) {
return <h1>Hola, {name}</h1>;
}
// 5. Export por defecto - arrow function (en dos pasos)
const Greeting = ({ name }: { name: string }) => (
<h1>Hola, {name}</h1>
);
export default Greeting;
// 6. Export nombrado
export function Greeting({ name }: { name: string }) {
return <h1>Hola, {name}</h1>;
}
// 7. Export nombrado - arrow function
export const Greeting = ({ name }: { name: string }) => (
<h1>Hola, {name}</h1>
);Cuándo recurrir a esto: No necesitas elegir un único estilo de forma exclusiva - pero deberías entender qué te da cada forma y elegir de manera consistente dentro de un proyecto.
function UserCard({ name, email }: { name: string; email: string }) {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}UserCard por encima de la línea donde se defineUserCard por su nombrethis - irrelevante para los componentes, pero significa que puede usarse con .bind() si hiciera faltaconst UserCard = ({ name, email }: { name: string; email: string }) => {
return (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);
};this léxico - hereda this del ámbito circundante (irrelevante para los componentes de función, pero importa en los callbacks de componentes de clase)constconst para todoconst UserCard = ({ name, email }: { name: string; email: string }) => (
<div>
<h2>{name}</h2>
<p>{email}</p>
</div>
);return - todo el cuerpo es una única expresiónexport default function Page() {
return <main>Inicio</main>;
}page.tsx, layout.tsx, loading.tsx, error.tsxPage, no Anonymousconst Page = () => {
return <main>Inicio</main>;
};
export default Page;// Adjuntar propiedades estáticas
const Tabs = ({ children }: { children: React.ReactNode }) => {
return <div>{children}</div>;
};
Tabs.Panel = TabPanel;
Tabs.List = TabList;
export default Tabs;export function UserCard({ name }: { name: string }) {
return <h2>{name}</h2>;
}import { UserCard } from "./user-card"// components/status.tsx - varios componentes relacionados
export function StatusBadge({ status }: { status: string }) {
return <span className="badge">{status}</span>;
}
export function StatusIcon({ status }: { status: string }) {
return <span className="icon">{status === "active" ? "🟢" : "🔴"}</span>;
}export const UserCard = ({ name }: { name: string }) => (
<h2>{name}</h2>
);export default function ({ name }: { name: string }) {
return <h2>{name}</h2>;
}Anonymous o _defaultReact.FC / React.FunctionComponent (legado)const UserCard: React.FC<{ name: string }> = ({ name }) => {
return <h2>{name}</h2>;
};children de forma implícita en los tipos más antiguos de React (previos a React 18)React.FC - ahora es equivalente al tipado directo| Forma | Con hoisting | Con nombre en DevTools | Tree-shakeable | Mejor para |
|---|---|---|---|---|
function Name() {} | Sí | Sí | Solo si se exporta | Uso general, páginas de Next.js |
const Name = () => {} | No | Sí (inferido) | Solo si se exporta | Equipos que prefieren const |
const Name = () => () | No | Sí (inferido) | Solo si se exporta | Componentes diminutos, solo con JSX |
export default function Name() | Sí | Sí | No (por defecto) | Páginas/layouts de Next.js |
export function Name() | Sí | Sí | Sí | Componentes compartidos, utilidades |
export const Name = () => {} | No | Sí (inferido) | Sí | Estilo const consistente |
React.FC<Props> | No | Sí (inferido) | Solo si se exporta | Solo código legado |
No hay diferencia funcional entre function y arrow para los componentes. A React le da igual. Elige según las convenciones de tu equipo:
Valores por defecto recomendados:
// Páginas, layouts y rutas de Next.js → export por defecto + declaración de función
export default function DashboardPage() { ... }
// Componentes compartidos → export nombrado + declaración de función
export function UserCard({ name }: UserCardProps) { ... }
// Helpers internos en el mismo archivo → declaración de función simple
function formatStatus(status: string) { ... }Si tu equipo usa arrow functions:
// Páginas de Next.js → sigue usando declaración de función por legibilidad
export default function DashboardPage() { ... }
// Todo lo demás → arrow
export const UserCard = ({ name }: UserCardProps) => { ... };
const formatStatus = (status: string) => { ... };Las declaraciones de función tienen hoisting - están disponibles antes de su posición textual en el archivo. Las arrow functions asignadas a const no.
// Esto funciona - la declaración de función tiene hoisting
export default function Page() {
return <Sidebar />;
}
function Sidebar() {
return <nav>Enlaces</nav>;
}// Esto falla - const NO tiene hoisting
export default function Page() {
return <Sidebar />; // ReferenceError: Cannot access 'Sidebar' before initialization
}
const Sidebar = () => <nav>Enlaces</nav>;En la práctica, esto rara vez importa porque la mayoría de los componentes están en archivos separados. Importa cuando tienes componentes helper debajo del export principal en el mismo archivo.
Las declaraciones de función y las arrow functions manejan los genéricos de forma diferente en TSX:
// Declaración de función - sintaxis limpia
function List<T>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }) {
return <ul>{items.map((item, i) => <li key={i}>{render(item)}</li>)}</ul>;
}
// Arrow function - necesita una coma final para diferenciarse del JSX
const List = <T,>({ items, render }: { items: T[]; render: (item: T) => React.ReactNode }) => {
return <ul>{items.map((item, i) => <li key={i}>{render(item)}</li>)}</ul>;
};El <T,> con una coma final es necesario porque <T> en un archivo .tsx se interpreta como una etiqueta JSX. La coma le indica a TypeScript que es un genérico. Este es un caso en el que las declaraciones de función tienen una clara ventaja sintáctica.
displayName para depuraciónLos componentes de orden superior y React.memo pueden perder el nombre del componente. Establece displayName explícitamente:
const UserCard = React.memo(({ name }: { name: string }) => (
<h2>{name}</h2>
));
UserCard.displayName = "UserCard";
// Con una declaración de función, el nombre se preserva automáticamente
const UserCard = React.memo(function UserCard({ name }: { name: string }) {
return <h2>{name}</h2>;
});
// displayName es automáticamente "UserCard" - no hace falta asignarlo manualmenteforwardRef// Expresión de función dentro de forwardRef
const Input = React.forwardRef<HTMLInputElement, InputProps>(
function Input({ label, ...props }, ref) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}
);
// Arrow function dentro de forwardRef
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ label, ...props }, ref) => (
<label>
{label}
<input ref={ref} {...props} />
</label>
)
);
// React 19: ref es una prop normal - no hace falta forwardRef
function Input({ label, ref, ...props }: InputProps & { ref?: React.Ref<HTMLInputElement> }) {
return (
<label>
{label}
<input ref={ref} {...props} />
</label>
);
}Los Server Components pueden ser async - una sintaxis que solo funciona con declaraciones de función y arrow functions con cuerpo de bloque:
// Declaración de función - limpia
export default async function PostsPage() {
const posts = await getPosts();
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
// Arrow function - también funciona
const PostsPage = async () => {
const posts = await getPosts();
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
};
export default PostsPage;El retorno implícito (=> (...)) no puede ser async porque await es una sentencia, no una expresión.
Los exports por defecto anónimos pierden su nombre en DevTools y en los stack traces. export default function() {} aparece como Anonymous. Nombra siempre la función, incluso en los exports por defecto.
Las arrow functions necesitan <T,> para los genéricos en archivos .tsx. Sin la coma final, <T> se interpreta como un elemento JSX. Las declaraciones de función no tienen esta ambigüedad.
Los componentes con const no tienen hoisting. Si defines componentes helper debajo del export principal en el mismo archivo, usa declaraciones de función o mueve los helpers por encima del componente principal.
React.FC incluía children de forma implícita antes de React 18. Si estás migrando desde React 17, eliminar React.FC puede sacar a la luz errores de tipo donde se usaba children pero no se tipaba explícitamente.
El retorno implícito se rompe cuando añades lógica. const Card = () => (<div>...</div>) debe reescribirse como const Card = () => { const x = ...; return <div>...</div>; } cuando añades cualquier sentencia antes del JSX.
export default combinado con const es un proceso de dos pasos. No puedes escribir export default const Name = ... - es un error de sintaxis. Usa export default function Name() o declara primero el const y luego export default Name.
Las reglas de ESLint pueden entrar en conflicto. react/function-component-definition impone un estilo. prefer-arrow-callback puede entrar en conflicto. Alinea tu configuración de ESLint con la convención elegida por tu equipo para evitar cambios innecesarios.
Mezclar estilos en un mismo archivo confunde a los colaboradores. Si UserCard es una declaración de función y Avatar, justo debajo, es una arrow function, quien lo lea pierde tiempo preguntándose si la diferencia es intencionada. Sé consistente dentro de un archivo.
| Alternativa | Cuándo usarla |
|---|---|
| Componentes de clase | Bases de código legadas, error boundaries (hasta useErrorBoundary de React 19) |
Server Components (async function) | Obtención de datos en el momento del render sin JS de cliente |
| Componentes de orden superior (HOC) | Preocupaciones transversales (auth, logging) - menos comunes ahora con los hooks |
| Render props | Composición dinámica de comportamiento - mayormente reemplazada por los hooks |
Revisado por Chris St. John·Última actualización: 10 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥