Giới thiệu
Trong vài năm gần đây, kiến trúc full-stack React đã thay đổi rất mạnh nhờ sự phát triển của:
React Server Components
App Router
ISR
Edge Rendering
Streaming
Partial Prerendering
Bài viết này sẽ walkthrough cách xây dựng một blog platform hiện đại sử dụng:
Technology | Vai trò |
|---|---|
Next.js 16 | Framework chính |
Supabase | Database + Auth |
TailwindCSS | Styling |
TypeScript | Type safety |
MDX | Render content |
Vercel | Deployment |
Table of Contents
Kiến trúc tổng thể
Database schema
Rendering strategy
Caching
SEO
MDX rendering
Syntax highlighting
Image optimization
Deployment
Performance optimization
1. Kiến trúc tổng thể
Một kiến trúc phổ biến:
TEXT
TEXT
Client
↓
Next.js App Router
↓
Server Components
↓
Cached Data Layer
↓
SupabaseVì sao App Router quan trọng?
App Router cho phép:
Server Components
Streaming
Nested layouts
Parallel routes
Granular caching
Ví dụ route structure:
TEXT
TEXT
app/
├── blogs/
│ ├── page.tsx
│ ├── [slug]/
│ │ └── page.tsx
│ └── loading.tsx
├── layout.tsx
└── page.tsx2. Database Schema
Posts table
TEXT
TEXT
create table posts (
id uuid primary key default gen_random_uuid(),
title text not null,
slug text unique not null,
content text not null,
created_at timestamptz default now()
);Categories
TEXT
TEXT
create table categories (
id uuid primary key default gen_random_uuid(),
name text not null
);Many-to-many relation
TEXT
TEXT
create table post_categories (
post_id uuid references posts(id),
category_id uuid references categories(id)
);3. Rendering Strategy
Listing Page
TEXT
TEXT
/blogs?page=2&category=reactNên dùng:
Dynamic route
Search params
Cached data layer
Ví dụ
TEXT
TEXT
const posts = await getBlogPosts({
page,
category,
})Detail Page
TEXT
TEXT
/blogs/nextjs-isr-guideNên dùng:
ISR
generateStaticParams
tag revalidation
4. Caching
unstable_cache
TEXT
TEXT
import { unstable_cache } from 'next/cache'
export const getBlogPosts = unstable_cache(
async () => {
return await db.posts.findMany()
},
['posts'],
{
tags: ['posts'],
revalidate: 3600,
}
)Revalidate tags
TEXT
TEXT
revalidateTag('posts')Điều này sẽ invalidate:
homepage
blog listing
related posts
recent posts sidebar
nếu cùng dùng tag posts.
5. SEO
Metadata API
TEXT
TEXT
export async function generateMetadata() {
return {
title: 'My Blog',
description: 'Modern web development articles',
}
}Open Graph
TEXT
TEXT
openGraph: {
images: ['/cover.png']
}Structured Data
TEXT
TEXT
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Next.js ISR Guide"
}6. MDX Rendering
Example Markdown
Inline code
Use unstable_cache carefully with authenticated requests.
Bold / italic
Bold text
Italic text
Bold italic
Blockquote
Đây là một blockquote để test typography renderer.
Nested quote:
Đây là nested blockquote level 2.
Task List
Setup Next.js
Configure Supabase
Add comments
Add analytics
Ordered List
Install dependencies
Setup environment
Create database schema
Deploy application
Nested List
Frontend
React
Next.js
App Router
Server Components
Backend
Supabase
PostgreSQL
7. Syntax Highlighting
TypeScript
TEXT
TEXT
type Post = {
id: string
title: string
slug: string
}
async function getPosts(): Promise<Post[]> {
const res = await fetch('/api/posts')
return res.json()
}Bash
TEXT
TEXT
pnpm install
pnpm devJSON
TEXT
TEXT
{
"name": "blog-platform",
"private": true
}Diff
TEXT
TEXT
- old cache strategy
+ unstable_cache with tags8. Tables
Basic Table
Feature | Supported |
|---|---|
ISR | ✅ |
SSR | ✅ |
RSC | ✅ |
Edge Runtime | ✅ |
Alignment Table
Left | Center | Right |
|---|---|---|
A | B | C |
1 | 2 | 3 |
9. Images
Standard Image
Image with Caption
Figure 1: Modern full-stack architecture.
10. Callouts
[!NOTE]
Đây là note callout.
[!WARNING]
Không nên dùngcookies()trong cached public data.
[!TIP]
DùngrevalidateTagthay vìrevalidatePathcho CMS/blog.
11. Horizontal Rules
Section separated.
Another separator.
12. Math Rendering
Inline math:
Kích thước cache complexity là .
Block math:
13. HTML Edge Cases
<div align="center">
<strong>Centered HTML content</strong>
</div>
<br />
<details>
<summary>Expandable Section</summary>
Hidden content inside details tag.
</details>
14. Long Content Paragraph
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt, nisl eget ultricies ultricies, nunc nisl aliquam nunc, vitae aliquam nisl nunc vitae nisl. Sed euismod, nisl eget aliquam ultricies, nunc nisl aliquam nunc, vitae aliquam nisl nunc vitae nisl.
Mauris feugiat, magna at interdum consequat, velit sapien tincidunt lectus, sed fermentum turpis massa sit amet odio. Donec vel malesuada arcu. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae.
15. Performance Optimization
Image Optimization
TEXT
TEXT
import Image from 'next/image'
<Image
src='/cover.webp'
alt='Cover'
width={1200}
height={630}
/>Streaming
TEXT
TEXT
<Suspense fallback={<PostsSkeleton />}>
<Posts />
</Suspense>Parallel Fetching
TEXT
TEXT
const postsPromise = getPosts()
const categoriesPromise = getCategories()
const [posts, categories] = await Promise.all([
postsPromise,
categoriesPromise,
])16. Conclusion
Một blog platform hiện đại thường sẽ có:
Dynamic listing pages
ISR detail pages
Cached data layer
Tag-based invalidation
MDX rendering
SEO optimization
Kiến trúc này vừa:
scalable
maintainable
SEO-friendly
performant
đồng thời tận dụng tốt toàn bộ ecosystem của React và Next.js hiện đại.
FAQ
Có nên dùng revalidatePath cho blog?
Thông thường không. revalidateTag scalable hơn.
Có nên static toàn bộ listing pages?
Không cần với đa số app.
Có nên dùng generateStaticParams?
Có, cho:
hot posts
newest posts
high traffic pages
unstable_cache có production-ready không?
Có. Rất nhiều app lớn vẫn dùng.
