Saltar al contenido

Contentful & Astro

Contentful es un CMS headless que te permite gestionar contenido, integrarte con otros servicios y publicar en múltiples plataformas.

En esta sección, utilizaremos el SDK de Contentful para conectar tu espacio de Contentful a Astro con cero JavaScript en el lado del cliente.

Para empezar, necesitarás lo siguiente:

  1. Un proyecto de Astro - Si aún no tienes un proyecto de Astro, nuestra guía de instalación te pondrá en marcha en poco tiempo.

  2. Una cuenta de Contentful y un espacio de Contentful. Si no tienes una cuenta, puedes registrarte para obtener una cuenta gratuita y crear un nuevo espacio de Contentful. También puedes usar un espacio existente si ya tienes uno.

  3. Credenciales de Contentful - Puedes encontrar las siguientes credenciales en tu panel de control de Contentful en Settings > API keys. Si no tienes ninguna clave de API, crea una seleccionando Add API key.

    • ID de espacio de Contentful - El ID de tu espacio de Contentful.
    • Token de acceso de entrega de Contentful - El token de acceso para consumir contenido publicado desde tu espacio de Contentful.
    • Token de acceso de vista previa de Contentful - El token de acceso para consumir contenido no publicado desde tu espacio de Contentful.

Para añadir las credenciales de tu espacio de Contentful a Astro, crea un archivo .env en la raíz de tu proyecto con las siguientes variables:

.env
CONTENTFUL_SPACE_ID=YOUR_SPACE_ID
CONTENTFUL_DELIVERY_TOKEN=YOUR_DELIVERY_TOKEN
CONTENTFUL_PREVIEW_TOKEN=YOUR_PREVIEW_TOKEN

Ahora, puedes usar estas variables de entorno en tu proyecto.

Si deseas tener IntelliSense para tus variables de entorno de Contentful, puedes crear un archivo env.d.ts en el directorio src/ y configurar ImportMetaEnv de esta manera:

src/env.d.ts
interface ImportMetaEnv {
readonly CONTENTFUL_SPACE_ID: string;
readonly CONTENTFUL_DELIVERY_TOKEN: string;
readonly CONTENTFUL_PREVIEW_TOKEN: string;
}

Tu directorio raíz ahora debería incluir estos nuevos archivos:

  • Directoriosrc/
    • env.d.ts
  • .env
  • astro.config.mjs
  • package.json

Para conectarte con tu espacio de Contentful, instala ambos de los siguientes usando el único comando a continuación para tu gestor de paquetes preferido:

Ventana de la terminal
npm install contentful @contentful/rich-text-html-renderer

A continuación, crea un nuevo archivo llamado contentful.ts en el directorio src/lib/ de tu proyecto.

src/lib/contentful.ts
import * as contentful from "contentful";
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});

El fragmento de código anterior crea un nuevo cliente de Contentful, pasando las credenciales del archivo .env.

Finalmente, tu directorio raíz ahora debería incluir estos nuevos archivos:

  • Directoriosrc/
    • env.d.ts
    • Directoriolib/
      • contentful.ts
  • .env
  • astro.config.mjs
  • package.json

Los componentes de Astro pueden obtener datos de tu cuenta de Contentful utilizando contentfulClient y especificando el content_type.

Por ejemplo, si tienes un tipo de contenido “blogPost” que tiene un campo de texto para un título y un campo de texto enriquecido para el contenido, tu componente podría verse así:

---
import { contentfulClient } from "../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { EntryFieldTypes } from "contentful";
interface BlogPost {
contentTypeId: "blogPost",
fields: {
title: EntryFieldTypes.Text
content: EntryFieldTypes.RichText,
}
}
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---
<body>
{entries.items.map((item) => (
<section>
<h2>{item.fields.title}</h2>
<article set:html={documentToHtmlString(item.fields.content)}></article>
</section>
))}
</body>

Puedes encontrar más opciones de consulta en la documentación de Contentful.

Con la configuración anterior, ahora puedes crear un blog que use Contentful como CMS.

  1. Un espacio de Contentful - Para este tutorial recomendamos comenzar con un espacio vacío. Si ya tienes un modelo de contenido, siéntete libre de usarlo, pero tendrás que modificar nuestros fragmentos de código para que coincidan con tu modelo de contenido.
  2. Un proyecto de Astro integrado con el SDK de Contentful - Consulta integrar con Astro para obtener más detalles sobre cómo configurar un proyecto de Astro con Contentful.

Dentro de tu espacio de Contentful, en la sección Content model, crea un nuevo modelo de contenido con los siguientes campos y valores:

  • Nombre: Publicación de blog
  • API identifier: blogPost
  • Descripción: Este tipo de contenido es para una publicación de blog

En tu tipo de contenido recién creado, utiliza el botón Add Field para añadir 5 campos nuevos con los siguientes parámetros:

  1. Text field
    • Nombre: title
    • Identificador de API: title (deja los otros parámetros con sus valores por defecto)
  2. Date and time field
    • Nombre: date
    • API identifier: date
  3. Text field
    • Nombre: slug
    • Identificador de API: slug (deja los otros parámetros con sus valores por defecto)
  4. Text field
    • Nombre: description
    • API identifier: description
  5. Rich text field
    • Nombre: content
    • API identifier: content

Haz clic en Save para guardar tus cambios.

En la sección Content de tu espacio de Contentful, crea una nueva entrada haciendo clic en el botón Add Entry. Luego, completa los campos:

  • Title: Astro is amazing!
  • Slug: astro-is-amazing
  • Descripción: Astro es un nuevo generador de sitios estáticos que es extremadamente rápido y fácil de usar.
  • Date: 2022-10-05
  • Content: This is my first blog post!

Haz clic en Publish para guardar tu entrada. Acabas de crear tu primera publicación de blog.

Siéntete libre de añadir tantas publicaciones de blog como desees, luego cambia a tu editor de código favorito para empezar a programar con Astro!

Crea una nueva interfaz llamada BlogPost y añádela a tu archivo contentful.ts en src/lib/. Esta interfaz coincidirá con los campos de tu tipo de contenido de publicación de blog en Contentful. La utilizarás para tipar la respuesta de tus entradas de publicación de blog.

src/lib/contentful.ts
import * as contentful from "contentful";
import type { EntryFieldTypes } from "contentful";
export interface BlogPost {
contentTypeId: "blogPost",
fields: {
title: EntryFieldTypes.Text
content: EntryFieldTypes.RichText,
date: EntryFieldTypes.Date,
description: EntryFieldTypes.Text,
slug: EntryFieldTypes.Text
}
}
export const contentfulClient = contentful.createClient({
space: import.meta.env.CONTENTFUL_SPACE_ID,
accessToken: import.meta.env.DEV
? import.meta.env.CONTENTFUL_PREVIEW_TOKEN
: import.meta.env.CONTENTFUL_DELIVERY_TOKEN,
host: import.meta.env.DEV ? "preview.contentful.com" : "cdn.contentful.com",
});

A continuación, ve a la página de Astro donde obtendrás los datos de Contentful. Utilizaremos la página de inicio index.astro en src/pages/ en este ejemplo.

Importa la interfaz BlogPost y contentfulClient desde src/lib/contentful.ts.

Obtén todas las entradas de Contentful con un tipo de contenido de blogPost mientras pasas la interfaz BlogPost para tipar tu respuesta.

src/pages/index.astro
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
---

Esta llamada de obtención devolverá un array con tus publicaciones de blog en entries.items. Puedes utilizar map() para crear un nuevo array (posts) que dé formato a los datos devueltos.

El siguiente ejemplo devuelve las propiedades items.fields de nuestro modelo de contenido para crear una vista previa de la publicación del blog y, al mismo tiempo, reformatea la fecha a un formato más legible.

src/pages/index.astro
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => {
const { title, date, description, slug } = item.fields;
return {
title,
slug,
description,
date: new Date(date).toLocaleDateString()
};
});
---

Finalmente, puedes usar posts en tu plantilla para mostrar una vista previa de cada publicación de blog.

src/pages/index.astro
---
import { contentfulClient } from "../lib/contentful";
import type { BlogPost } from "../lib/contentful";
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const posts = entries.items.map((item) => {
const { title, date, description, slug } = item.fields;
return {
title,
slug,
description,
date: new Date(date).toLocaleDateString()
};
});
---
<html lang="en">
<head>
<title>My Blog</title>
</head>
<body>
<h1>My Blog</h1>
<ul>
{posts.map((post) => (
<li>
<a href={`/posts/${post.slug}/`}>
<h2>{post.title}</h2>
</a>
<time>{post.date}</time>
<p>{post.description}</p>
</li>
))}
</ul>
</body>
</html>

Generación de publicaciones de blog individuales

Sección titulada “Generación de publicaciones de blog individuales”

Utiliza el mismo método para obtener tus datos de Contentful que se muestra arriba, pero esta vez, en una página que creará una ruta de página única para cada publicación de blog.

Si estás utilizando el modo estático por defecto de Astro, utilizarás rutas dinámicas y la función getStaticPaths(). Esta función se llamará en el momento de la compilación para generar la lista de rutas que se convertirán en páginas.

Crea un nuevo archivo llamado [slug].astro en src/pages/posts/.

Al igual que hiciste en index.astro, importa la interfaz BlogPost y contentfulClient desde src/lib/contentful.ts.

Esta vez, obtén tus datos dentro de una función getStaticPaths().

src/pages/posts/[slug].astro
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
}
---

Luego, mapea cada elemento a un objeto con una propiedad params y otra props. La propiedad params se utilizará para generar la URL de la página y la propiedad props se pasará al componente de la página como props.

src/pages/posts/[slug].astro
---
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const entries = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = entries.items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
content: documentToHtmlString(item.fields.content),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
---

La propiedad dentro de params debe coincidir con el nombre de la ruta dinámica. Dado que nuestro nombre de archivo es [slug].astro, utilizamos slug.

En nuestro ejemplo, el objeto props pasa tres propiedades a la página:

  • title (una cadena)
  • content (un documento de texto enriquecido convertido a HTML utilizando documentToHtmlString)
  • date (formateada utilizando el constructor Date)

Finalmente, puedes usar las props de la página para mostrar tu publicación de blog.

src/pages/posts/[slug].astro
---
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
export async function getStaticPaths() {
const { items } = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
});
const pages = items.map((item) => ({
params: { slug: item.fields.slug },
props: {
title: item.fields.title,
content: documentToHtmlString(item.fields.content),
date: new Date(item.fields.date).toLocaleDateString(),
},
}));
return pages;
}
const { content, title, date } = Astro.props;
---
<html lang="en">
<head>
<title>{title}</title>
</head>
<body>
<h1>{title}</h1>
<time>{date}</time>
<article set:html={content} />
</body>
</html>

¡Navega a http://localhost:4321/ y haz clic en una de tus publicaciones para asegurarte de que tu ruta dinámica esté funcionando!

Si has optado por el renderizado bajo demanda con un adaptador, utilizarás una ruta dinámica que usa un parámetro slug para obtener los datos de Contentful.

Crea una página [slug].astro en src/pages/posts. Utiliza Astro.params para obtener el slug de la URL, luego pásalo a getEntries:

src/pages/posts/[slug].astro
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
---

Si no se encuentra la entrada, puedes redirigir al usuario a la página 404 utilizando Astro.redirect.

src/pages/posts/[slug].astro
---
import { contentfulClient } from "../../lib/contentful";
import type { BlogPost } from "../../lib/contentful";
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
} catch (error) {
return Astro.redirect("/404");
}
---

Para pasar los datos de la publicación a la sección de la plantilla, crea un objeto post fuera del bloque try/catch.

Utiliza documentToHtmlString para convertir el content de un Document a HTML, y utiliza el constructor Date para formatear la fecha. title se puede dejar tal cual. Luego, añade estas propiedades a tu objeto post.

src/pages/posts/[slug].astro
---
import Layout from "../../layouts/Layout.astro";
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = {
title,
date: new Date(date).toLocaleDateString(),
content: documentToHtmlString(content),
};
} catch (error) {
return Astro.redirect("/404");
}
---

Finalmente, puedes hacer referencia a post para mostrar tu publicación de blog en la sección de la plantilla.

src/pages/posts/[slug].astro
---
import Layout from "../../layouts/Layout.astro";
import { contentfulClient } from "../../lib/contentful";
import { documentToHtmlString } from "@contentful/rich-text-html-renderer";
import type { BlogPost } from "../../lib/contentful";
let post;
const { slug } = Astro.params;
try {
const data = await contentfulClient.getEntries<BlogPost>({
content_type: "blogPost",
"fields.slug": slug,
});
const { title, date, content } = data.items[0].fields;
post = {
title,
date: new Date(date).toLocaleDateString(),
content: documentToHtmlString(content),
};
} catch (error) {
return Astro.redirect("/404");
}
---
<html lang="en">
<head>
<title>{post?.title}</title>
</head>
<body>
<h1>{post?.title}</h1>
<time>{post?.date}</time>
<article set:html={post?.content} />
</body>
</html>

Para desplegar tu sitio web, visita nuestras guías de despliegue y sigue las instrucciones para tu proveedor de alojamiento preferido.

Si tu proyecto utiliza el modo estático por defecto de Astro, necesitarás configurar un webhook para activar una nueva compilación cuando tu contenido cambie. Si utilizas Netlify o Vercel como tu proveedor de alojamiento, puedes utilizar su función de webhook para activar una nueva compilación a partir de eventos de Contentful.

Para configurar un webhook en Netlify:

  1. Ve al panel de control de tu sitio y haz clic en Build & deploy.

  2. En la pestaña Continuous Deployment, busca la sección Build hooks y haz clic en Add build hook.

  3. Proporciona un nombre para tu webhook y selecciona la rama en la que deseas activar la compilación. Haz clic en Save y copia la URL generada.

Para configurar un webhook en Vercel:

  1. Ve al panel de control de tu proyecto y haz clic en Settings.

  2. En la pestaña Git, busca la sección Deploy Hooks.

  3. Proporciona un nombre para tu webhook y la rama en la que deseas activar la compilación. Haz clic en Add y copia la URL generada.

En la configuración de tu espacio de Contentful, haz clic en la pestaña Webhooks y crea un nuevo webhook haciendo clic en el botón Add Webhook. Proporciona un nombre para tu webhook y pega la URL del webhook que copiaste en la sección anterior. Finalmente, presiona Save para crear el webhook.

Ahora, cada vez que publiques una nueva publicación de blog en Contentful, se activará una nueva compilación y tu blog se actualizará.

Más guías de CMS

Socios de CMS destacados

  • CloudCannon

    CMS basado en Git creado para mayor velocidad, seguridad y cero dolores de cabeza.

Todas las guías de CMS

Contribuir Comunidad Patrocinar