Ship a Site on Cosmic with Next.js

Build a blog on Next.js that reads from your Cosmic bucket. Covers the SDK, lean queries with props, static generation with generateStaticParams, and deploying to Vercel.
This lesson takes the content sitting in your Cosmic bucket and puts it on a deployed Next.js site. By the end you will have a blog index and individual post pages, statically generated, running on Vercel and reading from your bucket.
This is a developer lesson. It assumes you can run commands in a terminal and are comfortable with React.
Before You Start
-
Node.js 18 or later
-
A Cosmic bucket with a content type that has a few published objects in it. is used throughout; substitute your own slug
-
A body field on that type. New object types come with only a title and a slug, so add a Markdown metafield with the key , which is what the examples below read
-
Your bucket slug and read key, from Settings > API keys in your bucket
-
A Vercel account for the final step
Step 1: Create the Project
The flag matters. This lesson uses the App Router, and the data fetching is different from the older Pages Router.
Step 2: Add Your Keys
Create in the project root:
There is no prefix, which is deliberate. These variables stay on the server and never reach the browser.
Your bucket also has a write key. It is not needed here, and it should never appear in a frontend project. Anything with a write key can modify your content.
Step 3: Create the Client
Add :
One client, imported wherever you need it.
Step 4: Build the Index Page
Create :
Run and open .
Note what is absent. There is no , no loading state, and no API route in the middle. The component is an async server function, so the fetch runs on the server and the browser receives finished HTML.
tells Next.js to regenerate the page at most once a minute. Publishing in Cosmic updates the site without a redeploy.
Step 5: Fetch Only What You Render
controls which fields come back. Without it you get the entire object, including every metafield, on every post.
On a list page you rarely need at all. If your cards show a title, a slug, and a thumbnail, ask for exactly that:
Dotted paths work, so you can reach into and pull one field instead of the whole object. On a bucket with long post bodies this is the difference between a fast list page and a slow one.
Step 6: Add Dynamic Post Pages
Create :
Four things in here are worth calling out.
is the Markdown metafield you added, and printing it directly renders the raw markdown. On a real site you would run it through a renderer such as . Both the Markdown and Rich Text metafield types return markdown, so this applies either way.
is the App Router's way of listing which dynamic routes to build ahead of time. If you have used the Pages Router, this replaces . They are not interchangeable, and in an App Router project simply does nothing.
is a Promise and has to be awaited. This changed in Next.js 15. If you are following an older tutorial that reads directly, that is why it breaks.
resolves related objects. If your post has an author relationship, depth 1 returns the full author object instead of just its ID. Leave it off when you do not need it, since it costs response size.
A Tip for Empty Results
This is the single most common thing to trip over on a first Cosmic build.
When a query matches nothing, the SDK does not hand back an empty array. It throws a 404:
Both and behave this way. So a page for a slug that does not exist, or a list page for a content type with no published objects yet, will crash rather than render empty.
That is why the post page above wraps the call in and falls through to . For a list page, do the same and render an empty state:
If your build fails with a 404 from Cosmic and the query looks right, check whether anything is actually published. Draft objects are not returned by default.
Step 7: Deploy
From the project root:
Answer the prompts to link the project. The first deploy will fail to fetch content, because Vercel does not have your environment variables yet.
Add them:
Then ship it:
Your site is live and pulling from your bucket.
Going Further
Right now the site refreshes content on a 60 second timer. You can make it immediate instead: Cosmic can fire a webhook when an object is published, and Next.js can revalidate a specific path on demand when that webhook arrives. That turns a publish in the dashboard into an updated page within seconds, with no timer and no rebuild.
The method is the other thing worth knowing about, since it lets you request unpublished objects for a preview environment while production keeps serving only published content.
Do This in Your Project
-
Run
-
Install the SDK with
-
Put your bucket slug and read key in
-
Add with the client from Step 3
-
Build and confirm your posts list at
-
Add with
-
Deploy with , add the two environment variables, then
If you get a 404 from Cosmic at any point, re-read the tip above before debugging anything else.
Up Next
Lesson 5: Ship a Site on Cosmic with Astro
Lesson 5 builds the same site in Astro, which handles data fetching and static routes differently. Skip it if Next.js is your framework.
Up next
Ship a Site on Cosmic with Astro