I Installed Sanity Into Astro Without AI. The Point Wasn't the Installation.
If you have a donkey, will you carry your own bags? No? So...why not use AI? Still..you should be able to pick the bags up if you need.

Watch the accompanying video: The Sky Didn't Cave In – Installing Sanity Into Astro Without AI.
I remember the old days of wrestling with a project.
You'd jump between documentation, forums, Stack Overflow, blog posts, and whatever scraps of information you could find that looked vaguely related to what you were trying to achieve. Eventually you'd stitch together enough knowledge to get the thing working.
There was a certain pride in that process.
You were constantly balancing your intention against what you didn't know. Every project felt like a puzzle. Every solution felt earned.
Looking back, though, most of us weren't engineering masterpieces. I don't know that I've ever looked back on a project without thinking of a way to have improved it.
We were hashing things together.
A snippet from one forum. A recommendation from another developer. A chunk of documentation that seemed relevant. Eventually we'd arrive at an outcome that worked.
And then we'd all quietly agree on the universal developer rule:
It's working. Don't touch it.
Despite the imperfections, there was genuine satisfaction in figuring things out for yourself.
Today, things are very different.
The AI Debate Misses The Point
There's a lot of discussion around AI-assisted development.
Some developers see it as cheating while others see it as the future.
Personally, I think both sides are missing something and the truth -as is so often the case- is found in the grey; everything in moderation.
The question often up for debate is whether AI can write code better, faster, and with fewer mistakes than the average human.
And for many common development tasks, the answer is simply yes.
When I'm building Astro websites today, I regularly use Claude Code, Gemini, and other AI tools. Why wouldn't I? They're extraordinarily capable.
They save time.
They reduce repetitive work.
They eliminate countless opportunities for human error. As far as I'm concerned, I don't know if Jerry -the team developer- is having an off-day, feeling a little slower, a bit distracted or just hungry and rushing for lunch. Human error, even for repeated and familiar tasks, is simply a random occurrence.
All this in mind, refusing to use AI would be like insisting on carrying bricks by hand when somebody has offered you a forklift.
Efficiency matters.
Results matter.
If AI can help me produce a better outcome for a client in half the time, why wouldn't I use it?
But there is a danger lurking underneath all of that convenience.
Not because AI is bad.
Because dependency is.
What I Decided To Do Manually And Why
One of the tasks I regularly hand over to AI is integrating Sanity CMS with Astro.
It's a perfect AI job.
The setup process involves configuration files, package installation, schema creation, content structures, and lots of precise typing. Really, nothing is particularly difficult but there are lots of opportunities to waste time, make silly errors and go around in circles for no reason.
AI, however, handles it brilliantly; it's fast, flawless and incredibly efficient on all metrics for the task.
But recently I found myself wondering:
Could I still do it myself?
Could I figure it out?
Could I still wrestle with the code the way we used to?
So I decided to find out.
I opened a fresh Astro project.
I turned off the shortcuts.
And I installed Sanity manually.
What Actually Happened?
Exactly what you'd expect happened.
It was slower.
I got stuck - on stupid concepts or details that I could've spoken about in a tutorial beforehand.
There were moments of frustration.
There were moments where I knew I'd done something before but couldn't immediately remember how.
There were moments where I stared at the documentation wondering why it couldn't just tell me what I needed to know.
Meanwhile, somewhere in the back of my mind, I knew Claude could have completed the entire task before I'd raised my coffee to my lips.
But something interesting happened.
I've probably set up fifteen or twenty Astro and Sanity projects recently, but most of what I remembered from those projects was watching Claude race through terminal commands while I tended to other bits and pieces.
I understood the outcome.
I didn't fully understand the journey.
Doing it manually changed that.
I didn't just learn which commands to run (again).
I learned why they existed (again).
I understood the architecture more clearly (again).
I understood the relationships between different parts of the configuration (again).
I understood the decisions AI was making on my behalf (because "use it or lose it" was at play and I had definitely become blurry on the details). My understanding had faded.
And that understanding matters.
The Gym Analogy
I'm a big believer that struggle creates adaptation.
You don't get stronger by watching somebody else lift weights.
You get stronger by lifting them yourself.
Coding isn't much different.
The struggle isn't always pleasant.
It's often inefficient.
Sometimes it's frustrating.
But it develops understanding.
The problem is that many developers are swinging too far in one direction.
Some refuse to use AI because they think manual effort is somehow morally superior.
Others are becoming completely dependent on AI and losing touch with the underlying systems they're building.
Neither approach feels right to me.
My 10-80-10 Philosophy
The workflow I keep coming back to is what I call the 10-80-10 approach. See the 10-80-10 AI Web Design Workflow Here.
The first 10% is understanding.
Understanding the architecture.
Understanding the goals.
Understanding the decisions that need to be made.
Using that to give AI the resources, prompts and constraints to build professionally.
The middle 80% is where AI shines.
Let it handle the repetitive work.
Let it generate the boilerplate (I mean...most of us were grabbing snippets from older projects anyway, right?!)
Let it automate the tasks that add little value.
Then comes the final 10%.
Review.
Refine.
Adjust.
Question assumptions.
Make the changes AI couldn't quite get right.
That final 10% requires understanding.
And understanding comes from occasionally doing the work yourself.
The Donkey Principle
I mentioned this in the video available at the top and bottom of this article.
If you own a donkey, you're not going to carry your own bags.
That would be ridiculous.
The donkey is faster.
The donkey is stronger.
The donkey can do the heavy lifting.
But if the donkey falls over halfway up the mountain, you'd better be capable of carrying those bags yourself. It's helpful for you to have spent some time in the gym, getting the reps in on the squats, deadlifts and core.
That's how I view AI.
Use it.
Absolutely use it.
Use it every day.
Use it aggressively.
But don't become so dependent on it that you lose the ability to think independently.
Why This Matters Beyond Coding
For me, this isn't really about Sanity or Astro.
It's about a broader philosophy.
I've spent years watching businesses become dependent on platforms, page builders, SaaS products and ecosystems they don't fully control.
The more dependent you become, the more vulnerable you become.
The same principle applies to developers.
Learn the fundamentals.
Understand the systems.
Build the confidence that comes from knowing you can solve problems yourself.
Then use every tool available to accelerate the process.
That's the balance.
The Sky Didn't Cave In
The biggest takeaway from this exercise wasn't that I successfully installed Sanity manually, which is a rather basic task.
It was proving to myself that I could. I now live in a world where I constantly find myself affirming my thought processes with AI to ensure I'm not putting my foot wrong. I've become dependent on AI, like an apprentice asking if I should press the next button, even for tasks I once handled alone.
The process was slower.
It was bumpier.
It was occasionally frustrating.
But I walked the road myself.
Tomorrow I'll use Claude Code to do most of the work again.
And that's fine.
Because now I understand the terrain a little better.
In a world increasingly powered by AI, that's a skill (and mindset) worth protecting.
The sky didn't cave in.
I came away more confident than before.
Now, when I choose to use Claude to whack my schemas and fields together, it's an efficiency, not a dependency.
Watch the accompanying video: The Sky Didn't Cave In – Installing Sanity Into Astro Without AI.
How to install Sanity CMS manually to an Astro Project
Phase 1: Build the Static Blog Files
Step 1: Create the Blog Card Component
Create a new file at src/components/PostCard.astro. This is the reusable layout shell for your grid.
---
// src/components/PostCard.astro
interface Props {
title: string;
excerpt: string;
slug: string;
}
const { title, excerpt, slug } = Astro.props;
---
<article style="padding: 1.5rem; border: 1px solid #eee; margin-bottom: 1rem; border-radius: 6px;">
<h2><a href={`/blog/${slug}`}>{title}</a></h2>
<p>{excerpt}</p>
</article>
Step 2: Create the Top Blog Feed Page
Create a new file at src/pages/blog/index.astro. We will use a regular local JavaScript array inside the frontmatter so it loads perfectly without any databases.
---
// src/pages/blog/index.astro
import PostCard from '../../components/PostCard.astro';
// Purely local mock data for designing your layout
const localPosts = [
{
title: "My First Landscape Project",
excerpt: "A look at how we transformed a standard suburban garden into a functional green space.",
slug: "first-landscape-project"
},
{
title: "Essential Spring Garden Maintenance",
excerpt: "Top tips for preparing your soil and lawns for the upcoming growing season.",
slug: "spring-maintenance-tips"
}
];
---
<main style="max-width: 800px; margin: 3rem auto; padding: 0 1rem;">
<h1>Our Blog</h1>
<div class="feed">
{localPosts.map((post) => (
<PostCard
title={post.title}
excerpt={post.excerpt}
slug={post.slug}
/>
))}
</div>
</main>
Step 3: Create the Single Post Template Page
Create a new file at src/pages/blog/[slug].astro. For now, we will use Astro's standard static path generator with local data so you can click through your cards without crashing.
---
// src/pages/blog/[slug].astro
export async function getStaticPaths() {
// Temporary local paths that match our mock array above
return [
{ params: { slug: 'first-landscape-project' }, props: { title: "My First Landscape Project", content: "Full details about the landscape project go here..." } },
{ params: { slug: 'spring-maintenance-tips' }, props: { title: "Essential Spring Garden Maintenance", content: "Full details about spring maintenance go here..." } }
];
}
const { title, content } = Astro.props;
---
<main style="max-width: 800px; margin: 3rem auto; padding: 0 1rem;">
<h1>{title}</h1>
<p>{content}</p>
<a href="/blog">← Back to Blog</a>
</main>
Phase 2: Run the Check
Open your terminal and fire up your standard Astro development server:
npm run dev
- Go to
http://localhost:4321/blog. You will see your two mock posts loading beautifully. - Click on a post title. The dynamic router will smoothly take you to the article page.
Your entire frontend blog system is now fully constructed, operational, and running locally using zero external dependencies.
Get your Home, About, and these new Blog styles looking exactly how you want them in FEWD Studio, then take the next clean step to wire up the Sanity database connection!
Phase 3: Install & Connect Sanity
Step 1: Run the Integration Installer
Open your terminal inside your project directory and run this command to add the required bridge packages:
npm install @sanity/astro @sanity/client @astrojs/react
Step 2: Authenticate Your Machine
Log your terminal into your Sanity profile so it has permission to talk to your cloud projects:
npx sanity login
What to expect: This will open a browser window. Log in using your GitHub account. Once the browser says "Authenticated", close it and return to your terminal.
Step 3: Add the Configuration Mappings
We need to drop your Project ID from sanity.io/manage into your configurations and fix that type reference we hit earlier.
- Open your
src/env.d.tsfile and add this single line at the very bottom so Astro recognizes the client:
/// <reference types="@sanity/astro/module" />
Open your astro.config.mjs file and update it to look like this:
import { defineConfig } from 'astro/config';
import sanity from '@sanity/astro';
import react from '@astrojs/react';
export default defineConfig({
integrations: [
sanity({
projectId: 'PASTE_YOUR_PROJECT_ID_HERE',
dataset: 'production',
useCdn: false,
studioBasePath: '/studio',
}),
react(),
],
});
Create a brand new file named exactly sanity.config.ts right in your main project root folder (next to your package.json):
import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
export default defineConfig({
name: 'default',
title: 'Garden Services Studio',
projectId: 'PASTE_YOUR_PROJECT_ID_HERE',
dataset: 'production',
basePath: '/studio',
plugins: [structureTool()],
schema: {
types: [
{
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
{ name: 'title', title: 'Title', type: 'string' },
{ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'title' } },
{ name: 'excerpt', title: 'Excerpt', type: 'string' }
]
}
],
},
});
Phase 4: Flip the Switch to Live Data
Now that the bridge is built, we swap out your mock data for live queries.
Step 4: Update Your Blog Index Feed
Open your src/pages/blog/index.astro file, remove the localPosts array, and replace it with the live query:
---
// src/pages/blog/index.astro
import { sanityClient } from 'sanity:client';
import PostCard from '../../components/PostCard.astro';
const posts = await sanityClient.fetch(`*[_type == "post"] | order(_createdAt desc)`);
---
<main style="max-width: 800px; margin: 3rem auto; padding: 0 1rem;">
<h1>Our Blog</h1>
<div class="feed">
{posts.length === 0 ? (
<p>No posts found. Go to <a href="/studio">the studio</a> to write one!</p>
) : (
posts.map((post: any) => (
<PostCard
title={post.title}
excerpt={post.excerpt}
slug={post.slug.current}
/>
))
)}
</div>
</main>
Step 5: Update Your Dynamic Route
Open your src/pages/blog/[slug].astro (or your renamed version) and hook up the dynamic fetch:
---
// src/pages/blog/[slug].astro
import { sanityClient } from 'sanity:client';
export async function getStaticPaths() {
const posts = await sanityClient.fetch(`*[_type == "post" && defined(slug.current)]`);
return posts.map((post: any) => ({
params: { slug: post.slug.current },
props: { post },
}));
}
const { post } = Astro.props;
---
<main style="max-width: 800px; margin: 3rem auto; padding: 0 1rem;">
<h1>{post.title}</h1>
<p>{post.excerpt}</p>
<a href="/blog">← Back to Blog</a>
</main>
Final Verification
- Run
npm run devin your terminal. - Open
http://localhost:4321/studioin your browser. Log in via GitHub, click Blog Post, create a new document, fill in the data, click Generate on the slug, and hit Publish. - Go to
http://localhost:4321/blogto see your database content feeding seamlessly into your design.
Your Sanity Account
You need a Sanity account to hold the data for your project.
If you don't have one yet, here is how to get it sorted right now:
- Go to sanity.io in your browser and click Sign Up.
- Create your account using whichever method you prefer (Google, GitHub, or standard E-mail).
- Once you are logged into the web dashboard, you will see your default workspace. You can then instantly return to your terminal and run
npx sanity loginusing those same credentials.
Step 1: Add the Project ID to Astro
- Open your
astro.config.mjsfile. - Replace
'PASTE_YOUR_PROJECT_ID_HERE'with your actual Sanity project ID (eyn1n13a).
Step 2: Add the Project ID to Sanity Configuration
- Open your
sanity.config.tsfile (located in your main root directory next topackage.json). - Replace
'PASTE_YOUR_PROJECT_ID_HERE'with the same ID (eyn1n13a).
Step 3: Boot the Server
Run the dev command in your terminal:
npm run dev
Step 4: Add Your Content
- Head over to
http://localhost:4321/studioin your browser. - Click on Blog Post, hit Create New, and type out a test title and excerpt.
- Click Generate next to the slug field, and click Publish.
Once that post is published, go straight to http://localhost:4321/blog to see your design automatically pull the title and excerpt from your live Sanity project!
Ensure your astro.config file contains this
// @ts-check
import { defineConfig } from 'astro/config';
import sanity from '@sanity/astro';
import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
integrations: [
sanity({
projectId: 'eyn1n13a',
dataset: 'production',
useCdn: false,
studioBasePath: '/studio',
}),
react(),
],
});
Next Steps:
- Make sure your
sanity.config.tsfile in your root folder also has your project ID (eyn1n13a) added to it. - Ensure you have added the type reference line
/// <reference types="@sanity/astro/module" />to the very bottom of yoursrc/env.d.tsfile.
Once those three files are saved, go ahead and boot the server up with npm run dev and navigate to http://localhost:4321/studio to verify the connection!
Don't have Sanity.config.ts? Neither did I...I don't know why!
Step 1: Create the file
- Click the
Garden-Servicesroot folder at the top of your file tree to make sure it's selected. - Click the
File+button at the bottom of the sidebar. - Name the new file exactly:
sanity.config.ts
Step 2: Add the Configuration Code
Open your newly created sanity.config.ts file and paste the following setup code into it:
import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
export default defineConfig({
name: 'default',
title: 'Garden Services Studio',
projectId: 'eyn1n13a',
dataset: 'production',
basePath: '/studio',
plugins: [structureTool()],
schema: {
types: [
{
name: 'post',
title: 'Blog Post',
type: 'document',
fields: [
{ name: 'title', title: 'Title', type: 'string' },
{ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'title' } },
{ name: 'excerpt', title: 'Excerpt', type: 'string' }
]
}
],
},
});
Save that file, and you're good to go! Once you run npm run dev in your terminal, the embedded studio will be fully ready to load at `
CORS
Because you are embedding the studio directly into your local website, your browser is going to make authenticated data requests from http://localhost:4321 to Sanity's API. You have to tell Sanity to trust your local development port so it doesn't block it with a CORS error.
Since your terminal is already logged in, you can add this instantly via the command line.
The Fast Terminal Fix
Run this command in your project terminal:
npx sanity cors add http://localhost:4321
- Important: When the terminal asks you
Allow credentials? (y/n), typeyand hit enter. The studio interface won't load properly unless credentials are explicitly enabled.
You may need to use this instead:
npx sanity cors add http://localhost:4321 --project-id=eyn1n13a --credentials
Alternative: Web Dashboard Method
This is what I've generally done with Claude (he'd tell me to put my coffee down and do some work).
If you'd rather do it via the browser, you can add it manually:
- Go to sanity.io/manage and select your project (
eyn1n13a). - Click API (or Settings > API settings) in the navigation menu.
- Under CORS Origins, click Add CORS origin.
- Enter exactly:
http://localhost:4321(do not add a trailing slash). - Check the box for Allow credentials.
- Click Save.
Once that's added, run npm run dev and open http://localhost:4321/studio—you'll be greeted by your operational, authenticated local Sanity Studio panel.
Time to Log In and Test it
- Click on the login provider button matching how you just set up your Sanity account (Google, GitHub, or E-mail).
- Once authorized, the white box will change into your Garden Services Studio content panel.
- Click on Blog Post -> Create New.
- Type out a test title and an excerpt, then hit Generate next to the slug field so it mirrors the title.
- Hit the green Publish button at the bottom right.
Once you have published that test post, head over to http://localhost:4321/blog to verify that your Astro layout is successfully pulling the live data!
You may now need to (be prompted to) log in on the dev server.
Now that you have hit Publish in the studio, the post is live in your Sanity database.
To view it, you just need to visit the public routing pages you set up earlier:
1. The Blog Index Feed
Open a new browser tab or navigate to your local site's blog feed to see the post card:
- URL:
http://localhost:4321/blog - What you should see: The
PostCardlayout displaying the test title and excerpt you just typed out.
2. The Dynamic Post Page
To view the full article page, either click the card from the feed page or go directly to its dynamic URL:
- URL:
http://localhost:4321/blog/your-test-slug - (Replace
your-test-slugwith the exact text that was generated in the slug field inside the studio.)
If you run into a 404 or the content doesn't appear, make sure the dev server is still running in your FEWD Studio terminal panel!
Enjoyed this?
Let me know. Send me a message. Comment on YouTube.