Astro is a modern web framework for building ultra-fast, content-rich websites with zero JavaScript by default and seamless integration of your favorite UI components
Strapi is the leading open-source headless CMS offering features, like customizable APIs, role-based permissions, multilingual support, etc. It simplifies content management and integrates effortlessly with modern frontend frameworks.
Explore the Strapi documentation for more details.
Astro is a modern, open-source web framework designed for building fast, content-driven, SEO optimized, websites such as blogs, marketing pages, and e-commerce platforms.

Its standout โislands architectureโ serves static HTML by default and only loads JavaScript for interactive components, delivering exceptionally fast load times and an improved user experience compared to traditional JavaScript-heavy frameworks.
Visit the Astro.js documentation for more.
The out-of-the-box Strapi features allow you to get up and running in no time:
Learn more about Strapi features.
We are going to start by setting up our Strapi 5 project with the following command:
๐๏ธ Note: make sure that you have created a new directory for your project.
You can find the full documentation for Strapi 5 here.
npx create-strapi-app@latest serverYou will be asked to choose if you would like to use Strapi Cloud we will choose to skip for now.
๐ Welcome to Strapi! Ready to bring your project to life?
Create a free account and get:
30 days of access to the Growth plan, which includes:
โจ Strapi AI: content-type builder, media library and translations
โ
Live Preview
โ
Single Sign-On (SSO) login
โ
Content History
โ
Releases
? Please log in or sign up.
Login/Sign up
โฏ SkipAfter that, you will be asked how you would like to set up your project. We will choose the following options:
? Do you want to use the default database (sqlite) ? Yes
? Start with an example structure & data? Yes <-- make sure you say yes
? Start with Typescript? Yes
? Install dependencies with npm? Yes
? Initialize a git repository? YesOnce everything is set up and all the dependencies are installed, you can start your Strapi server with the following command:
cd server
npm run developYou will be greeted with the Admin Create Account screen.

Go ahead and create your first Strapi user. All of this is local so you can use whatever you want.
Once you have created your user, you will be redirected to the Strapi Dashboard screen.

Since we created our app with the example data, you should be able to navigate to your Article collection and see the data that was created for us.

Now, let's make sure that all of the data is published. If not, you can select all items via the checkbox and then click the Publish button.

Once all your articles are published, we will expose our Strapi API for the Articles Collection. This can be done in Settings -> Users & Permissions plugin -> Roles -> Public -> Article.
You should have find and findOne selected. If not, go ahead and select them.

Now, if we make a GET request to http://localhost:1337/api/articles, we should see the following data for our articles.

๐๏ธ Note: that article covers (images) are not returned. This is because the REST API by default does not populate any relations, media fields, components, or dynamic zones.. Learn more about REST API: Population & Field Selection.
So let's get the article covers by using the populate=* parameter: http://localhost:1337/api/articles?populate=*

Nice, now that we have our Strapi 5 server setup, we can start to setup our Astro.js application.
It is recommended to use the npm create astro@latest, which sets up everything automatically for you.
npm create astro@latestDepending on your project setup, you will answer the following prompts:
aul@dev astro npm create astro@latest
Need to install the following packages:
create-astro@4.13.2
Ok to proceed? (y) y
> npx
> "create-astro"
astro Launch sequence initiated.
dir Where should we create your new project?
./client
tmpl How would you like to start your new project?
A basic, helpful starter project
deps Install dependencies?
Yes
git Initialize a new git repository?
Yes
โโโโโโ Project initializing...
โ Template copied
โถ Dependencies installing with npm...
โก Git
โ Project initialized!
โ Template copied
โ Dependencies installed
โ Git initialized
next Liftoff confirmed. Explore your project!
Enter your project directory using cd ./client
Run npm run dev to start the dev server. CTRL+C to stop.
Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
โญโโโโโโฎ Houston:
โ โ โก โ Good luck out there, astronaut! ๐
โฐโโโโโโฏ
Nice, now that we have the project setup, let's install Tailwind CSS.
Let's add Tailwind with the following command npx astro add tailwind.
paul@dev astro npx astro add tailwind
Need to install the following packages:
astro@5.16.9
Ok to proceed? (y) y
โ Resolving packages...
15:00:11 [add] Unable to locate a config file, generating one for you.
Astro will make the following changes to your config file:
โญ astro.config.mjs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ import { defineConfig } from 'astro/config'; โ
โ import tailwindcss from '@tailwindcss/vite'; โ
โ // https://astro.build/config โ
โ export default defineConfig({ โ
โ vite: { โ
โ plugins: [tailwindcss()] โ
โ } โ
โ }); โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
To run this command without prompts, pass the --yes flag
โ Continue? โฆ yes
success Added the following integration to your project:
- tailwind
action required You must import your Tailwind stylesheet, e.g. in a shared layout:
โญ src/layouts/Layout.astro โโโโโโโฎ
โ --- โ
โ import '../styles/global.css' โ
โ --- โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
Make sure to import tailwindcss into src/styles/global.css to make Tailwind classes available to your Astro project.
src/styles/global.css
@import "tailwindcss";Import this file in the pages where you want Tailwind to apply. This is often done in a layout component so that Tailwind styles can be used on all pages sharing that layout:
src/layouts/Layout.astro
---
import "../styles/global.css";
---Nice, we are now ready to build our app.
There are several ways to load data from Strapi in your Astro application. Let's explore three common approaches:
But first, let's define our TypeScript types for type safety across all methods.
Create a types file to define the structure of your Strapi API response:
src/types/strapi.ts
interface Image {
id: number;
documentId: string;
name: string;
alternativeText: string;
width: number;
height: number;
url: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
}
interface Author {
id: number;
documentId: string;
name: string;
email: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
}
interface Category {
id: number;
documentId: string;
name: string;
slug: string;
description: string | null;
createdAt: string;
updatedAt: string;
publishedAt: string;
}
interface RichTextBlock {
__component: "shared.rich-text";
id: number;
body: string;
}
interface QuoteBlock {
__component: "shared.quote";
id: number;
title: string;
body: string;
}
interface MediaBlock {
__component: "shared.media";
id: number;
}
interface SliderBlock {
__component: "shared.slider";
id: number;
}
type Block = RichTextBlock | QuoteBlock | MediaBlock | SliderBlock;
export interface Article {
id: number;
documentId: string;
title: string;
description: string;
slug: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
cover: Image;
author: Author;
category: Category;
blocks: Block[];
}
interface Pagination {
page: number;
pageSize: number;
pageCount: number;
total: number;
}
export interface ArticlesResponse {
data: Article[];
meta: {
pagination: Pagination;
};
}Now let's look at the different ways to fetch data from Strapi.
The simplest way to load data from Strapi is using the native fetch API directly in your Astro components.
src/pages/index.astro
---
import Layout from "../layouts/Layout.astro";
import type { ArticlesResponse } from "../types/strapi";
const STRAPI_URL = "http://localhost:1337";
const response = await fetch(`${STRAPI_URL}/api/articles?populate=*`);
const { data: articles }: ArticlesResponse = await response.json();
---
<Layout>
<main class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold mb-8 text-center">Articles</h1>
<ul class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{articles.map((article) => (
<li class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
<img
src={`${STRAPI_URL}${article.cover.url}`}
alt={article.cover.alternativeText}
class="w-full h-48 object-cover"
/>
<div class="p-5">
<span class="inline-block px-3 py-1 text-xs font-semibold text-blue-600 bg-blue-100 rounded-full mb-3">
{article.category.name}
</span>
<h2 class="text-xl font-bold mb-2 text-gray-800">{article.title}</h2>
<p class="text-gray-600 text-sm mb-4 line-clamp-2">{article.description}</p>
<div class="flex items-center justify-between text-sm text-gray-500">
<span>By {article.author.name}</span>
<span>{new Date(article.publishedAt).toLocaleDateString()}</span>
</div>
</div>
</li>
))}
</ul>
</main>
</Layout>Tip: For production, store your Strapi URL in environment variables:
.env
STRAPI_URL=http://localhost:1337Then use it in your components:
---
import type { ArticlesResponse } from "../types/strapi";
const response = await fetch(`${import.meta.env.STRAPI_URL}/api/articles?populate=*`);
const { data: articles }: ArticlesResponse = await response.json();
---You should see the following:

The Strapi Client SDK provides a typed client for interacting with Strapi APIs with better developer experience and type safety.
First, install the package:
npm install @strapi/clientCreate a Strapi client instance:
src/lib/strapi.ts
import { strapi } from "@strapi/client";
export const client = strapi({
baseURL: "http://localhost:1337/api",
});Use the client in your Astro components:
src/pages/index.astro
---
import Layout from "../layouts/Layout.astro";
import { client } from "../lib/strapi";
const STRAPI_URL = "http://localhost:1337";
const { data: articles } = await client.collection("articles").find({
populate: "*",
});
---
<Layout>
<main class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold mb-8 text-center">Articles</h1>
<ul class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{articles.map((article) => (
<li class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
<img
src={`${STRAPI_URL}${article.cover.url}`}
alt={article.cover.alternativeText}
class="w-full h-48 object-cover"
/>
<div class="p-5">
<span class="inline-block px-3 py-1 text-xs font-semibold text-blue-600 bg-blue-100 rounded-full mb-3">
{article.category.name}
</span>
<h2 class="text-xl font-bold mb-2 text-gray-800">{article.title}</h2>
<p class="text-gray-600 text-sm mb-4 line-clamp-2">{article.description}</p>
<div class="flex items-center justify-between text-sm text-gray-500">
<span>By {article.author.name}</span>
<span>{new Date(article.publishedAt).toLocaleDateString()}</span>
</div>
</div>
</li>
))}
</ul>
</main>
</Layout>Notice that we still get our data like before.

The Strapi Client SDK also supports:
client.single("homepage")Learn more in the Strapi Client documentation.
Finally let's try the Strapi Community Astor Loader.
The Strapi Community Astro Loader integrates Strapi collections with Astro's Content Layer API, providing type-safe access to your Strapi content.
First, install the package:
npm install strapi-community-astro-loader@3.0.0-rc.0Configure the loader in your Astro content config:
src/content.config.ts
import { defineCollection, z } from "astro:content";
import { strapiLoader } from "strapi-community-astro-loader";
const imageSchema = z.object({
id: z.number(),
documentId: z.string(),
name: z.string(),
alternativeText: z.string().nullable(),
width: z.number(),
height: z.number(),
url: z.string(),
});
const authorSchema = z.object({
id: z.number(),
documentId: z.string(),
name: z.string(),
email: z.string(),
});
const categorySchema = z.object({
id: z.number(),
documentId: z.string(),
name: z.string(),
slug: z.string(),
});
const articleSchema = z.object({
title: z.string(),
description: z.string(),
slug: z.string(),
createdAt: z.string(),
updatedAt: z.string(),
publishedAt: z.string(),
cover: imageSchema,
author: authorSchema,
category: categorySchema,
});
const articles = defineCollection({
loader: strapiLoader({
contentType: "article",
clientConfig: { baseURL: "http://localhost:1337/api" },
params: { populate: "*" },
}),
schema: articleSchema,
});
export const collections = { articles };Notice that we needed to add our Zod schema for types.
Use the collection in your Astro components:
src/pages/index.astro
---
import Layout from "../layouts/Layout.astro";
import { getCollection } from "astro:content";
const STRAPI_URL = "http://localhost:1337";
const articles = await getCollection("articles");
---
<Layout>
<main class="container mx-auto px-4 py-8">
<h1 class="text-4xl font-bold mb-8 text-center">Articles</h1>
<ul class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{articles.map((article) => (
<li class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
<img
src={`${STRAPI_URL}${article.data.cover.url}`}
alt={article.data.cover.alternativeText}
class="w-full h-48 object-cover"
/>
<div class="p-5">
<span class="inline-block px-3 py-1 text-xs font-semibold text-blue-600 bg-blue-100 rounded-full mb-3">
{article.data.category.name}
</span>
<h2 class="text-xl font-bold mb-2 text-gray-800">{article.data.title}</h2>
<p class="text-gray-600 text-sm mb-4 line-clamp-2">{article.data.description}</p>
<div class="flex items-center justify-between text-sm text-gray-500">
<span>By {article.data.author.name}</span>
<span>{new Date(article.data.publishedAt).toLocaleDateString()}</span>
</div>
</div>
</li>
))}
</ul>
</main>
</Layout>Go ahead and restart your application after the update, everything should show up just like before.
Important: When using Astro's Content Layer API, all schema data is accessed via the
dataattribute (e.g.,article.data.title,article.data.cover.url). This is different from the fetch and SDK methods where you access properties directly on the article object.
Benefits of using the Strapi Loader:
getCollection and getEntry helpersLearn more in the Strapi Community Astro Loader documentation.
| Method | Best For |
|---|---|
| Fetch Directly | Simple projects, quick prototypes, or when you need full control |
| Strapi Client SDK | Projects needing type safety and a cleaner API for complex queries |
| Strapi Loader | Content-heavy sites leveraging Astro's Content Layer for caching and type generation |
Nice, now that you have a basic Astro project, let's checkout some community starters and examples.
The Strapi community has built some great tools and starters for Astro integration:
Special thanks to our friends at VirtusLab for their excellent open source contributions to the Strapi and Astro ecosystem.
| Resource | Description |
|---|---|
| Astro Strapi Starter | A full-featured starter template for building Astro sites with Strapi |
| Astro Strapi Loader | Content loader for integrating Strapi with Astro's Content Layer |
| Astro Strapi Blocks | Renderer for Strapi's block content in Astro components |
| Resource | Description |
|---|---|
| Strapi Community Astro Loader | Community-driven Astro loader for Strapi with automatic type inference |
| Strapi Client SDK | Official client for interacting with Strapi APIs |