visit
To create a new Astro project, run npm create astro@latest
. Astro provides a convenient to set up some boilerplate code and get your project configured quickly. You can choose from a few different templates, but in our case, we’re keeping it simple with the “Just the Basics” option configuring a nice organized directory, a few Astro components to reference, and an Astro config file.
# npm
npm install cosmicjs
# pnpm
pnpm add cosmicjs
# yarn
yarn add cosmicjs
Next, we will create two items in our project directory. First, create a lib folder in the src folder, and then create a cosmic.js file. After you’ve created this file, create a .env in the root of your directory. Here, we will store our environment variables from the Cosmic Bucket we’re about to set up, and in our cosmic.js file we will import the environment variables and create our API calls to retrieve our data.
Using the Astro CLI, we can run the commands npx astro add image
and npx astro add tailwind
, and Astro will install each individual package and update the astro.config.mjs file to support the integration.
Our default fields will contain the Slug field and the Content field, so you can leave these as they are.
Let’s create our first Post! Navigate to “Posts” in the sidebar and click “Add Post.” Fill in the Title, Excerpt, and Content with some sample text. Then, upload an image for the Cover Image and select “Publish.”
Before we leave the Cosmic dashboard, we need to grab a couple of things. On the sidebar, click on Settings>API Acess. To make this easier, I’m going to rename the Bucket Slug to something like my-astro-blog then click “Save Settings”.
Now we’re going to take the Bucket Slug and Read Key values and place them into our .env file in our Astro app.
# .env
PUBLIC_COSMIC_BUCKET_SLUG=my-astro-blog
PUBLIC_COSMIC_READ_KEY=<your_read_key_here>
Once set, let’s go into the cosmic.js file we created in Step 1. We will import the Cosmic module, instantiate an api variable and import our environment variables. Refer to for setting environment variables in Astro.
// src/lib/cosmic.js
import Cosmic from "cosmicjs";
const api = Cosmic();
const bucket = api.bucket({
slug: import.meta.env.PUBLIC_COSMIC_BUCKET_SLUG,
read_key: import.meta.env.PUBLIC_COSMIC_READ_KEY,
});
// src/lib/cosmic.js
export async function getAllPosts() {
const data = await bucket.objects
.find({
type: "posts",
})
.props("title,slug,content,metadata")
return data.objects;
}
Using Cosmic’s , we can find the specific type of Object in our API, which is “posts” (the slug of the Object itself), and set the props to all of the parameters we set up when we created our Object earlier. In our case, we want to retrieve the title, slug, content, and the Cover Image, which lives inside of the metadata.
Remember that the default parameters are accessible at a higher level than the metadata, and the metadata itself comes from the custom metafields you create within an Object. Grabbing the Cover Image would look like this: metadata.cover_image.imgix_url.
Now we can pull in the API call onto one of our Astro pages. By calling the getAllPosts
function in the “helmet” of our Astro page or component, we can access all of the content we create in Cosmic.
// src/pages/index.astro
import { getAllPosts } from "../lib/cosmic"
const data = await getAllPosts()
To render out a list of the posts we make, we can create a minimal Card component like this:
// src/components/Card.Astro
---
const { href, title, body } = Astro.props
---
<li class="bg-white rounded-md shadow-md hover:shadow-xl transition">
<a href={`/blog/${href}`}>
<div class="flex flex-col h-full gap-y-2 p-6">
<h2 class="font-semibold text-xl pr-4 pt-2">
{title}
</h2>
<p>
{body}
</p>
</div>
</a>
</li>
// src/pages/index.astro
---
import { getAllPosts } from "../lib/cosmic"
import Card from "../components/Card.astro";
const data = await getAllPosts()
---
<section>
<ul class="grid md:grid-cols-2 gap-8">
{
data.map((post) => (
<Card
title={post.title}
href={post.slug}
body={post.metadata.excerpt}
/>
))
}
</ul>
</section>
You’ll notice that we are passing the slug of our Cosmic post into the href prop. We can generate dynamic routes easily with Astro. We do this using , which will generate dynamic page routes at build time.
// src/pages/blolg/[slug].astro
---
import { getAllPosts } from "../../lib/cosmic";
import { Image } from "@astrojs/image/components";
export async function getStaticPaths() {
const data = (await getAllPosts()) || [];
return data.map((post) => {
return {
params: { slug: post.slug },
props: { post },
};
});
}
const { post } = Astro.props;
---
<article>
<section class="border-b pb-8">
<h1 class="text-4xl font-bold">{post.title}</h1>
<div class="my-8"></div>
</section>
<Image
src={post.metadata.cover_image.imgix_url}
format="webp"
width={1200}
aspectRatio={16 / 9}
quality={50}
alt=""
class={"rounded-md shadow-lg my-12"}
/>
<p>{post.content}</p>
</article>
Using getStaticPaths, we are generating the path for each “Post” in Cosmic using it’s slug. This happens at build time and uses the data stored in params. We then pass the data for each “Post” through to the props and further set the { post } object to Astro.props. We can re-use our HTML template below the helmet for each blog post we render.