Skip to content

Strapi & Astro

Add content to your Astro project using Strapi Headless CMS

Strapi is an open-source, customizable, headless CMS.

This guide will build a wrapper function to connect Strapi with Astro.

To get started, you will need to have the following:

  1. An Astro project - If you don’t have an Astro project yet, our Installation guide will get you up and running in no time.
  2. A Strapi CMS server - You can set up a Strapi server on a local environment.

To add your Strapi URL to Astro, create a .env file in the root of your project (if one does not already exist) and add the following variable:

.env
STRAPI_URL="http://127.0.0.1:1337" # or use your IP address

Restart the dev server to use this environment variable in your Astro project.

If you would like to have IntelliSense for your environment variable, you can create a env.d.ts file in the src/ directory and configure ImportMetaEnv like this:

src/env.d.ts
interface ImportMetaEnv {
readonly STRAPI_URL: string;
}

Your root directory should now include the new file(s):

Create a new file at src/lib/strapi.ts and add the following wrapper function to interact with the Strapi API:

src/lib/strapi.ts
interface Props {
endpoint: string;
query?: Record<string, string>;
wrappedByKey?: string;
wrappedByList?: boolean;
}
/**
* Fetches data from the Strapi API
* @param endpoint - The endpoint to fetch from
* @param query - The query parameters to add to the url
* @param wrappedByKey - The key to unwrap the response from
* @param wrappedByList - If the response is a list, unwrap it
* @returns
*/
export default async function fetchApi<T>({
endpoint,
query,
wrappedByKey,
wrappedByList,
}: Props): Promise<T> {
if (endpoint.startsWith('/')) {
endpoint = endpoint.slice(1);
}
const url = new URL(`${import.meta.env.STRAPI_URL}/api/${endpoint}`);
if (query) {
Object.entries(query).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const res = await fetch(url.toString());
let data = await res.json();
if (wrappedByKey) {
data = data[wrappedByKey];
}
if (wrappedByList) {
data = data[0];
}
return data as T;
}

This function requires an object with the following properties:

  1. endpoint - The endpoint you are fetching.
  2. query - The query parameters to add to the end of URL
  3. wrappedByKey - The data key in the object that wraps your Response.
  4. wrappedByList - A parameter to “unwrap” the list returned by Strapi, and return only the first item.

If you are using TypeScript, create the following Article interface to correspond to the Strapi content types at src/interfaces/article.ts:

src/interfaces/article.ts
export default interface Article {
documentId: number;
title: string;
description: string;
content: string;
slug: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
}

Create the file src/pages/blog/[slug].astro to dynamically generate a page for each article.

In Astro’s default static mode (SSG), use getStaticPaths() to fetch your list of articles from Strapi.

src/pages/blog/[slug].astro
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---

Create the template for each page using the properties of each post object.

src/pages/blog/[slug].astro
---
import fetchApi from '../../lib/strapi';
import type Article from '../../interfaces/article';
export async function getStaticPaths() {
const articles = await fetchApi<Article[]>({
endpoint: 'articles',
wrappedByKey: 'data',
});
return articles.map((article) => ({
params: { slug: article.slug },
props: article,
}));
}
type Props = Article;
const article = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.image.data.url} />
<h1>{article.title}</h1>
<!-- Render plain text -->
<p>{article.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.content} />
</main>
</body>
</html>

If you’ve opted into on-demand rendering with an adapter, generate your dynamic routes using the following code:

Create the src/pages/blog/[slug].astro file:

src/pages/blog/[slug].astro
---
import fetchApi from '../../../lib/strapi';
import type Article from '../../../interfaces/article';
const { slug } = Astro.params;
let article: Article;
try {
article = await fetchApi<Article>({
endpoint: 'articles',
wrappedByKey: 'data',
wrappedByList: true,
query: {
'filters[slug][$eq]': slug || '',
},
});
} catch (error) {
return Astro.redirect('/404');
}
---
<!DOCTYPE html>
<html lang="en">
<head>
<title>{article.title}</title>
</head>
<body>
<main>
<img src={import.meta.env.STRAPI_URL + article.image.data.url} />
<h1>{article.title}</h1>
<!-- Render plain text -->
<p>{article.content}</p>
<!-- Render markdown -->
<MyMarkdownComponent>
{article.content}
</MyMarkdownComponent>
<!-- Render html -->
<Fragment set:html={article.content} />
</main>
</body>
</html>

This file will fetch and render the page data from Strapi that matches the dynamic slug parameter.

Since you are using a redirect to /404, create a 404 page in src/pages:

src/pages/404.astro
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
<p>Sorry, this page does not exist.</p>
<img src="https://http.cat/404" />
</body>
</html>

If the article is not found, the user will be redirected to this 404 page and be greeted by a lovely cat.

To deploy your website, visit our deployment guides and follow the instructions for your preferred hosting provider.

If your project is using Astro’s default static mode, you will need to set up a webhook to trigger a new build when your content changes. If you are using Netlify or Vercel as your hosting provider, you can use its webhook feature to trigger a new build from Strapi.

To set up a webhook in Netlify:

To set up a webhook in Vercel:

Follow the Strapi webhooks guide to create a webhook in your Strapi admin panel.