
Làm website không cần WordPress: Astro + Sanity, kèm AI dựng giao diện
- Thẻ
- Ngày đăng
- Lượt xem
Trước đây, ai muốn có website mà không biết code thì gần như chỉ có một lựa chọn: WordPress. Nó quen quá rồi. Cài theme, thêm vài plugin, viết bài. Vậy là có web.
Rắc rối bắt đầu khi bạn muốn sửa theo ý mình. Đổi bố cục một trang thì phải đi tìm plugin page builder. Muốn web nhanh hơn thì cài plugin cache. Muốn SEO tốt thì thêm plugin SEO. Một thời gian sau site có 20 plugin, cái này đá cái kia, mỗi lần cập nhật lại phải ngồi cầu nguyện.
Giờ thì khác rồi. Có những framework như Astro giúp bạn làm giao diện đẹp và chuẩn SEO, còn phần code thì AI viết gần hết. Bạn chỉ cần mô tả mình muốn gì. Phần quản lý nội dung, tức chỗ bạn vào viết bài và đăng ảnh như trang admin của WordPress, thì giao cho Sanity.
Nói thêm: chính trang bạn đang đọc cũng chạy bằng Astro + Sanity. Nếu bạn muốn có một site như vầy mà không muốn tự làm, cứ liên hệ mình. Còn nếu thích tự tay làm thì đọc tiếp nhé.
Vì sao lại là Astro + Sanity?
- Astro tạo ra các trang HTML tĩnh. Người đọc mở trang là thấy ngay, không phải chờ database hay server xử lý gì. Google rất thích kiểu trang nhanh và sạch như vậy.
- Sanity là CMS, tức chỗ để viết và quản lý bài. Bạn có một trang admin để soạn bài, up ảnh, gắn tag, giống WordPress nhưng gọn hơn nhiều. Gói miễn phí đủ dùng cho blog cá nhân và site doanh nghiệp nhỏ.
- Hosting gần như miễn phí. Site tĩnh đưa lên Vercel hay Netlify với gói free là chạy được. Bạn không phải thuê server, cũng không phải lo update PHP.
- Không có plugin nào để bị hack. Phía người đọc chỉ có HTML, nên kẻ xấu không có trang wp-admin nào để dò mật khẩu.
Chuẩn bị
- Cài Node.js bản 22 trở lên (tải ở nodejs.org).
- Một tài khoản Sanity (sanity.io, đăng nhập bằng Google hoặc GitHub).
- Một tài khoản GitHub và một tài khoản Vercel để đưa site lên mạng.
- Một trình soạn code. Mình dùng VS Code với Claude Code để AI viết code giúp.
Bước 1: Tạo project Astro
npm create astro@latest my-site
cd my-siteKhi được hỏi template thì chọn mẫu trống (minimal). Muốn dùng TypeScript thì cứ chọn, AI viết TypeScript rất tốt.
Bước 2: Cài Sanity vào Astro
npx astro add @sanity/astro @astrojs/react
npm install sanity styled-components astro-portabletext@astrojs/react cần thiết vì trang admin của Sanity (gọi là Studio) được viết bằng React.
Bước 3: Tạo project trên Sanity
- Vào sanity.io/manage, bấm Create project.
- Chép lại Project ID. Dataset để tên mặc định là
productionvà chọn chế độ public, để lúc build site đọc được bài mà không cần token. - Vào API → CORS origins, thêm
http://localhost:4321và tick Allow credentials. Sau khi có domain thật thì thêm cả domain đó.
Tạo file .env ở thư mục gốc:
PUBLIC_SANITY_PROJECT_ID=project_id_cua_ban
PUBLIC_SANITY_DATASET=productionBước 4: Nối Astro với Sanity
Sửa file astro.config.mjs:
import react from '@astrojs/react';
import sanity from '@sanity/astro';
import { defineConfig } from 'astro/config';
import { loadEnv } from 'vite';
const env = loadEnv(process.env.NODE_ENV ?? 'development', process.cwd(), '');
export default defineConfig({
output: 'static',
integrations: [
sanity({
projectId: env.PUBLIC_SANITY_PROJECT_ID,
dataset: env.PUBLIC_SANITY_DATASET || 'production',
apiVersion: '2026-01-01',
useCdn: false,
studioBasePath: '/studio',
}),
react(),
],
});studioBasePath: '/studio' gắn trang admin vào ngay trong site. Chạy local thì vào localhost:4321/studio để viết bài. Không cần dựng thêm trang admin nào khác.
Bước 5: Định nghĩa "bài viết" trông như thế nào
Bên WordPress, khung của bài viết có sẵn. Còn ở Sanity, bạn tự định nghĩa bài viết gồm những trường nào. Nghe thì có vẻ mệt, nhưng chính chỗ này giúp bạn custom thoải mái.
Tạo file sanity/schemas/post.ts:
import { defineField, defineType } from 'sanity';
export const post = defineType({
name: 'post',
title: 'Bài viết',
type: 'document',
fields: [
defineField({ name: 'title', title: 'Tiêu đề', type: 'string' }),
defineField({ name: 'slug', title: 'Slug', type: 'slug', options: { source: 'title' } }),
defineField({ name: 'coverImage', title: 'Ảnh bìa', type: 'image' }),
defineField({ name: 'excerpt', title: 'Mô tả ngắn', type: 'text', rows: 3 }),
defineField({ name: 'publishedAt', title: 'Ngày đăng', type: 'datetime' }),
defineField({
name: 'body',
title: 'Nội dung',
type: 'array',
of: [{ type: 'block' }, { type: 'image' }],
}),
],
});Rồi tạo file sanity.config.ts ở thư mục gốc:
import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
import { post } from './sanity/schemas/post';
export default defineConfig({
name: 'my-site',
title: 'My Site',
projectId: import.meta.env.PUBLIC_SANITY_PROJECT_ID,
dataset: import.meta.env.PUBLIC_SANITY_DATASET || 'production',
plugins: [structureTool()],
schema: { types: [post] },
});Chạy npm run dev, mở localhost:4321/studio, đăng nhập rồi viết thử bài đầu tiên. Nhớ bấm Publish nhé.
Bước 6: Hiển thị bài viết ra site
Sanity dùng ngôn ngữ truy vấn tên là GROQ. Mới nhìn thì hơi lạ, nhưng đọc vài lần là quen.
Sửa trang chủ src/pages/index.astro:
---
import { sanityClient } from 'sanity:client';
const posts = await sanityClient.fetch(`
*[_type == "post" && !(_id in path("drafts.**"))] | order(publishedAt desc) {
title, "slug": slug.current, excerpt
}
`);
---
<h1>Blog của mình</h1>
<ul>
{posts.map((p) => (
<li><a href={`/posts/${p.slug}`}>{p.title}</a> <p>{p.excerpt}</p></li>
))}
</ul>Đoạn !(_id in path("drafts.**")) dùng để lọc bản nháp. Thiếu nó thì bài viết dở cũng bị đưa lên site.
Tạo thêm trang chi tiết src/pages/posts/[slug].astro:
---
import { sanityClient } from 'sanity:client';
import { PortableText } from 'astro-portabletext';
export async function getStaticPaths() {
const slugs = await sanityClient.fetch(
`*[_type == "post" && defined(slug.current)].slug.current`
);
return slugs.map((slug) => ({ params: { slug } }));
}
const post = await sanityClient.fetch(
`*[_type == "post" && slug.current == $slug][0]{ title, excerpt, body }`,
{ slug: Astro.params.slug }
);
---
<html lang="vi">
<head>
<title>{post.title}</title>
<meta name="description" content={post.excerpt} />
</head>
<body>
<h1>{post.title}</h1>
<PortableText value={post.body} />
</body>
</html>Lúc build, Astro tạo sẵn một file HTML cho mỗi bài. Khi có người đọc thì không cần gọi về Sanity nữa.
Bước 7: Để AI làm giao diện
Đến đây site đã chạy được nhưng trông còn trơn. Đây là lúc AI giúp nhiều nhất. Mở Claude Code (hoặc Cursor) ngay trong thư mục project rồi nói kiểu:
"Làm giao diện blog tối màu, kiểu tạp chí, font chữ có chân cho tiêu đề. Trang chủ hiện bài viết dạng lưới có ảnh bìa. Responsive trên điện thoại."
"Thêm thẻ Open Graph, canonical, sitemap và JSON-LD cho từng bài viết để SEO tốt."
Mẹo nhỏ: mỗi lần chỉ nhờ một việc, xem kết quả trên trình duyệt xong mới nhờ việc tiếp theo. Đừng bắt AI làm cả site trong một câu.
Bước 8: Đưa lên mạng
- Đẩy code lên GitHub.
- Vào Vercel → Add New Project, chọn repo, rồi thêm 2 biến
PUBLIC_SANITY_PROJECT_IDvàPUBLIC_SANITY_DATASETtrong phần Environment Variables. - Bấm Deploy. Vậy là có link.
- Vào sanity.io/manage → API → CORS origins, thêm domain Vercel của bạn để Studio trên site thật đăng nhập được.
Bài mới tự lên site: vì site là tĩnh, mỗi lần đăng bài phải build lại. Để khỏi làm tay, vào Vercel tạo một Deploy Hook (Settings → Git → Deploy Hooks). Sau đó vào Sanity tạo một Webhook (API → Webhooks) trỏ tới link đó. Từ giờ bạn bấm Publish trong Studio, khoảng một phút sau bài đã có trên site.
Tóm lại
- Bắt đầu: WordPress rất dễ. Astro + Sanity cần làm theo vài bước.
- Custom giao diện: WordPress phụ thuộc theme và plugin. Astro thì muốn gì cũng làm được, có AI viết code.
- Tốc độ và SEO: WordPress phải cài thêm plugin. Astro có sẵn vì là HTML tĩnh.
- Bảo mật: WordPress phải cập nhật thường xuyên. Site tĩnh gần như không có gì để hack.
- Chi phí: WordPress tốn hosting, theme, plugin. Astro + Sanity thì gói free đủ dùng.
Nếu bạn muốn có một site như vầy (blog, landing page, web giới thiệu doanh nghiệp) mà không có thời gian tự làm, nhắn mình nhé. Mình làm giúp, rồi hướng dẫn bạn tự viết bài trên Sanity.