Saltar al contenido

Migrar desde Next.js

Aquí hay algunos conceptos clave y estrategias de migración para ayudarte a empezar. ¡Usa el resto de nuestra documentación y nuestra comunidad de Discord para seguir adelante!

Next.js y Astro comparten algunas similitudes que te ayudarán a migrar tu proyecto:

Cuando reconstruyas tu sitio de Next.js en Astro, notarás algunas diferencias importantes:

  • Next.js es una single-page app de React, y usa index.js como raíz de tu proyecto. Astro es un sitio multi-página, e index.astro es tu página de inicio.

  • Los componentes .astro no se escriben como funciones exportadas que devuelven plantillas de página. En su lugar, dividirás tu código en un “code fence” para tu JavaScript y un body exclusivamente para el HTML que generas.

  • orientado a contenido: Astro fue diseñado para mostrar tu contenido y permitirte optar por la interactividad solo cuando sea necesario. Una app existente de Next.js podría estar construida para alta interactividad del lado del cliente y puede requerir técnicas avanzadas de Astro para incluir elementos que son más difíciles de replicar usando componentes .astro, como dashboards.

Cada migración de proyecto será diferente, pero hay algunas acciones comunes que realizarás al convertir de Next.js a Astro.

Usa el comando create astro de tu gestor de paquetes para lanzar el asistente CLI de Astro o elige un tema de la comunidad del Astro Theme Showcase.

Puedes pasar un argumento --template al comando create astro para iniciar un nuevo proyecto de Astro con uno de nuestros starters oficiales (p. ej. docs, blog, portfolio). O, puedes iniciar un nuevo proyecto desde cualquier repositorio existente de Astro en GitHub.

Ventana de la terminal
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>

Luego, copia los archivos existentes de tu proyecto de Next a tu nuevo proyecto de Astro en una carpeta separada fuera de src.

Puede que te resulte útil instalar algunas de las integraciones opcionales de Astro para usar mientras conviertes tu proyecto de Next a Astro:

  • @astrojs/react: para reutilizar algunos componentes de UI de React existentes en tu nuevo sitio de Astro, o seguir escribiendo con componentes de React.

  • @astrojs/mdx: para traer archivos MDX existentes de tu proyecto de Next, o para usar MDX en tu nuevo sitio de Astro.

Siguiendo la estructura de proyecto de Astro:

  1. Mantén la carpeta public/ de Next sin cambios.

    Astro usa el directorio public/ para assets estáticos, al igual que Next. No se necesita ningún cambio en esta carpeta ni en su contenido.

  2. Copia o mueve otros archivos y carpetas de Next (p. ej. pages, styles etc.) a la carpeta src/ de Astro mientras reconstruyes tu sitio, siguiendo la estructura de proyecto de Astro.

    Como Next, la carpeta src/pages/ de Astro es una carpeta especial usada para enrutamiento basado en archivos. Todas las demás carpetas son opcionales, y puedes organizar el contenido de tu carpeta src/ como quieras. Otras carpetas comunes en proyectos de Astro incluyen src/layouts/, src/components, src/styles, src/scripts.

Astro tiene un archivo de configuración en la raíz de tu proyecto llamado astro.config.mjs. Este se usa únicamente para configurar tu proyecto de Astro y cualquier integración instalada, incluyendo adaptadores SSR.

Aquí hay algunos tips para convertir un componente .js de Next en un componente .astro:

  1. Usa el JSX devuelto de la función del componente existente de Next.js como base para tu plantilla HTML.

  2. Cambia cualquier sintaxis de Next o JSX a sintaxis de Astro o a estándares web de HTML. Esto incluye <Link>, <Script>, {children}, y className, por ejemplo.

  3. Mueve cualquier JavaScript necesario, incluyendo declaraciones de importación, a un “code fence” (---). Nota: JavaScript para renderizar contenido condicionalmente a menudo se escribe directamente dentro de la plantilla HTML en Astro.

  4. Usa Astro.props para acceder a cualquier prop adicional que se pasara previamente a tu función de Next.

  5. Decide si algún componente importado también necesita convertirse a Astro. Con la integración oficial instalada, puedes usar componentes de React existentes en tu archivo de Astro. Pero, puede que quieras convertirlos a componentes .astro, ¡especialmente si no necesitan ser interactivos!

  6. Reemplaza getStaticProps() con declaraciones import o import.meta.glob() para consultar tus archivos locales. Usa fetch() para obtener datos externos.

Ve un ejemplo de un archivo .js de Next convertido paso a paso.

Compara el siguiente componente de Next y un componente de Astro correspondiente:

StarCount.jsx
import Header from "./header";
import Footer from "./footer";
import "./layout.css";
export async function getStaticProps() {
const res = await fetch("https://api.github.com/repos/withastro/astro");
const json = await res.json();
return {
props: { message: json.message, stars: json.stargazers_count || 0 },
}
}
const Component = ({ stars, message }) => {
return (
<>
<Header />
<p style={{
backgroundColor: `#f4f4f4`,
padding: `1em 1.5em`,
textAlign: `center`,
marginBottom: `1em`
}}>Astro has {stars} 🧑‍🚀</p>
<Footer />
</>
)
}
export default Component;

Puede que te resulte útil comenzar convirtiendo tus layouts y plantillas de Next.js en componentes de layout de Astro.

Next tiene dos métodos diferentes para crear archivos de layout, cada uno de los cuales maneja los layouts de manera diferente a Astro:

Cada página de Astro requiere explícitamente que las etiquetas <html>, <head>, y <body> estén presentes, así que es común reutilizar un archivo de layout entre páginas. Astro usa un <slot /> para el contenido de la página, sin necesidad de declaración de import. Ten en cuenta las plantillas HTML estándar, y el acceso directo a <head>:

src/layouts/Layout.astro
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>

Tu proyecto de Next puede tener un archivo pages/_document.jsx que importa componentes de React para personalizar el <head> de tu app:

pages/_document.jsx
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link rel="icon" href="/favicon.ico" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
  1. Crea un nuevo archivo de layout de Astro usando solo el JSX devuelto.

  2. Reemplaza cualquier componente de React con etiquetas <html>, <head>, <slot>, y otras etiquetas estándar de HTML.

    src/layouts/Document.astro
    <html lang="en">
    <head>
    <link rel="icon" href="/favicon.ico" />
    </head>
    <body>
    <slot/>
    </body>
    </html>

Los archivos de layout del directorio app/ de Next.js se crean con dos archivos: un archivo layout.jsx para personalizar los contenidos de <html> y <body>, y un archivo head.jsx para personalizar los contenidos del elemento <head>.

app/layout.jsx
export default function Layout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
app/head.jsx
export default function Head() {
return (
<>
<title>My Page</title>
</>
);
}
  1. Crea un nuevo archivo de layout de Astro usando solo el JSX devuelto.

  2. Reemplaza ambos archivos con un único archivo de layout de Astro que contenga un page shell (etiquetas <html>, <head>, y <body>) y un <slot/> en lugar de la prop {children} de React:

    src/layouts/Layout.astro
    <html lang="en">
    <head>
    <title>My Page</title>
    </head>
    <body>
    <slot/>
    </body>
    </html>

En Next.js, tus posts viven en /pages o en /app/routeName/page.jsx.

En Astro, todo el contenido de tus páginas debe vivir dentro de src/ a menos que uses content collections.

Tus páginas JSX (.js) de Next existentes necesitarán ser convertidas de archivos JSX a páginas .astro. No puedes usar un archivo de página JSX existente en Astro.

Estas páginas .astro deben estar ubicadas dentro de src/pages/ y tendrán rutas de página generadas automáticamente basadas en su ruta de archivo.

Astro tiene soporte integrado para Markdown y una integración opcional para archivos MDX. Puedes reutilizar cualquier archivo Markdown y MDX existente, pero pueden requerir algunos ajustes en su frontmatter, como añadir la propiedad especial de frontmatter layout de Astro. Ya no necesitarás crear páginas manualmente para cada ruta generada por Markdown. Estos archivos pueden colocarse dentro de src/pages/ para aprovechar el enrutamiento automático basado en archivos.

Alternativamente, puedes usar content collections en Astro para almacenar y gestionar tu contenido. Recuperarás el contenido tú mismo y generarás esas páginas dinámicamente.

Como Astro devuelve HTML crudo, es posible escribir tests end-to-end usando la salida del paso de build. Cualquier test end-to-end escrito previamente podría funcionar out-of-the-box si has podido igualar el markup de tu sitio de Next. Librerías de testing como Jest y React Testing Library pueden importarse y usarse en Astro para testear tus componentes de React.

Consulta la guía de testing de Astro para más.

Referencia: Convertir sintaxis de Next.js a Astro

Sección titulada “Referencia: Convertir sintaxis de Next.js a Astro”

Convierte cualquier componente de Next <Link to="">, <NavLink> etc. a etiquetas HTML <a href="">.

<Link to="/blog">Blog</Link>
<a href="/blog">Blog</a>

Astro no usa ningún componente especial para links, aunque eres libre de construir tu propio componente <Link>. Luego puedes importar y usar este <Link> igual que cualquier otro componente.

src/components/Link.astro
---
const { to } = Astro.props;
---
<a href={to}><slot /></a>

Actualiza cualquier importación de archivos para referenciar rutas de archivos relativas exactamente. Esto se puede hacer usando alias de importación, o escribiendo una ruta relativa completa.

Ten en cuenta que los archivos .astro y varios otros tipos de archivo deben importarse con su extensión completa.

src/pages/authors/Fred.astro
---
import Card from "../../components/Card.astro";
---
<Card />

Convierte cualquier instancia de {children} a un <slot /> de Astro. Astro no necesita recibir {children} como una prop de función y renderizará automáticamente el contenido hijo en un <slot />.

src/components/MyComponent.astro
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>

Los componentes de React que pasan múltiples conjuntos de children se pueden migrar a un componente de Astro usando named slots.

Ve más sobre uso específico de <slot /> en Astro.

Convierte cualquier instancia de getStaticProps() a import.meta.glob() o getCollection()/getEntry() para acceder a datos de otros archivos en el código fuente de tu proyecto. Para obtener datos remotos, usa fetch().

Estas solicitudes de datos se hacen en el frontmatter del componente de Astro y usan top-level await.

src/pages/index.astro
---
import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entries
const allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entries
const allPosts = Object.values(import.meta.glob('../pages/posts/*.md', { eager: true }));
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---

Ve más sobre importaciones de archivos locales con import.meta.glob(), consultas con content collections o obtención de datos remotos.

Puede que necesites reemplazar cualquier librería CSS-in-JS (p. ej. styled-components) con otras opciones de CSS disponibles en Astro.

Si es necesario, convierte cualquier objeto de estilo inline (style={{ fontWeight: "bold" }}) a atributos de estilo HTML inline (style="font-weight:bold;"). O, usa una etiqueta <style> de Astro para estilos CSS scoped.

src/components/Card.astro
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>

Tailwind es soportado después de instalar el plugin de Vite de Tailwind. ¡No se requieren cambios a tu código de Tailwind existente!

Ve más sobre estilos en Astro.

Convierte cualquier componente <Image /> de Next a el propio componente de imagen de Astro en archivos .astro o .mdx, o a una etiqueta HTML estándar <img> / JSX <img /> según corresponda en tus componentes de React.

El componente <Image /> de Astro funciona solo en archivos .astro y .mdx. Consulta una lista completa de sus atributos de componente y ten en cuenta que varios diferirán de los atributos de Next.

src/pages/index.astro
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">

En componentes de React (.jsx), usa sintaxis de imagen JSX estándar (<img />). Astro no optimizará estas imágenes, pero puedes instalar y usar paquetes NPM para más flexibilidad.

Puedes aprender más sobre el uso de imágenes en Astro en la guía de Images.

Aquí hay un ejemplo de data fetching de la Pokédex de Next.js convertido a Astro.

pages/index.js obtiene y muestra una lista de los primeros 151 Pokémon usando la REST PokéAPI.

Aquí te mostramos cómo recrear eso en src/pages/index.astro, reemplazando getStaticProps() con fetch().

  1. Identifica el JSX del return().

    pages/index.js
    import Link from 'next/link'
    import styles from '../styles/poke-list.module.css';
    export default function Home({ pokemons }) {
    return (
    <>
    <ul className={`plain-list ${styles.pokeList}`}>
    {pokemons.map((pokemon) => (
    <li className={styles.pokemonListItem} key={pokemon.name}>
    <Link className={styles.pokemonContainer} as={`/pokemon/${pokemon.name}`} href="/pokemon/[name]">
    <p className={styles.pokemonId}>No. {pokemon.id}</p>
    <img className={styles.pokemonImage} src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}></img>
    <h2 className={styles.pokemonName}>{pokemon.name}</h2>
    </Link>
    </li>
    ))}
    </ul>
    </>
    )
    }
    export const getStaticProps = async () => {
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    return {
    props: {
    pokemons,
    },
    }
    }
  2. Crea src/pages/index.astro

    Usa el valor de return de la función de Next. Convierte cualquier sintaxis de Next o React a Astro, incluyendo cambiar el caso de cualquier atributo global de HTML.

    Ten en cuenta que:

    • .map simplemente funciona!

    • className se convierte en class.

    • <Link> se convierte en <a>.

    • El fragment <> </> no es necesario en las plantillas de Astro.

    • key es un atributo de React, y no es un atributo de li en Astro.

    src/pages/index.astro
    ---
    ---
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem">
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
  3. Añade cualquier import, prop y JavaScript necesario

    Ten en cuenta que:

    • la función getStaticProps ya no es necesaria. Los datos de la API se obtienen directamente en el code fence.
    • Un componente <Layout> se importa y envuelve la plantilla de página.
    src/pages/index.astro
    ---
    import Layout from '../layouts/layout.astro';
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    ---
    <Layout>
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem" key={pokemon.name}>
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
    </Layout>

Más guías de migración

Contribuir Comunidad Patrocinar