A modern, bilingual blog platform built with Next.js, Tailwind CSS, and MDX. Perfect for creators, developers, and writers who want a lightweight, customizable blogging solution with Vietnamese and English language support.
Write your blog in Markdown with bilingual content support and ship it to everyone on the internet! β¨
- Framework: Next.js 15 with App Router
- Deployment: Vercel
- Styling: Tailwind CSS
- Content: MDX with metadata
- Languages: Vietnamese & English with dynamic switching
- Package Manager: pnpm
- π Write posts in MDX format with YAML frontmatter
- π Bilingual support - Vietnamese and English with language switcher
- π Smart language detection - Auto-detects browser language preference
- πΎ Language persistence - Saves language choice to localStorage
- π Multilingual blog posts - Support for tagged bilingual content in a single file
- β‘ Fast performance with Next.js - all static HTML files, 100% Core Web Vitals
- πΌοΈ Dynamic Open Graph image generation
- π° Auto-generated RSS and Atom feeds
- πΊοΈ Automatically generated sitemap
- π SEO-friendly with structured data (JSON-LD) and semantic HTML
- βΏ WCAG 2.2 accessibility compliant with proper semantic markup
- π¨ Customizable author branding and social links
- π± Responsive lightweight design with system-based dark mode
- Node.js v18.17 or higher
- pnpm (recommended) or npm
-
Clone the repository:
git clone https://github.com/yourusername/open-blog.git cd open-blog -
Copy the environment template and fill in your details:
cp .env.example .env.local
-
Edit
.env.localwith your personal information:NEXT_PUBLIC_SITE_URL=https://yourblog.com/ NEXT_PUBLIC_SITE_NAME=Your Blog Name NEXT_PUBLIC_SITE_DESCRIPTION=Your blog description NEXT_PUBLIC_AUTHOR_NAME=Your Name NEXT_PUBLIC_AUTHOR_EMAIL=[email protected] NEXT_PUBLIC_AUTHOR_IMAGE_URL=https://github.com/yourusername.png NEXT_PUBLIC_GITHUB_URL=https://github.com/yourusername/ NEXT_PUBLIC_LINKEDIN_URL=https://www.linkedin.com/in/yourprofile/ NEXT_PUBLIC_TWITTER_HANDLE=@yourhandle NEXT_PUBLIC_COPYRIGHT_YEAR=2025
-
Install dependencies:
pnpm install
-
Start the development server:
pnpm dev
Open http://localhost:3000 to view your blog.
# Start development server
pnpm dev
# Build for production
pnpm build
# Start production server
pnpm start
# Lint code
pnpm lint
# Format code
pnpm format
pnpm format:checkInkspired/
βββ src/
β βββ app/ # Next.js App Router pages
β β βββ blog/ # Blog posts directory
β β β βββ [slug]/ # Dynamic blog post folders
β β β β βββ page.mdx # Individual blog post content
β β β βββ layout.tsx # Blog layout wrapper
β β β βββ page.tsx # Blog index page
β β βββ layout.tsx # Root layout with LanguageProvider
β β βββ page.tsx # Homepage
β β βββ not-found.tsx # 404 page
β βββ components/ # React components
β β βββ bilingual-content.tsx # Bilingual content components
β β βββ blog-post-layout.tsx # Blog post wrapper with "Back" link
β β βββ footer.tsx # Footer with translations
β β βββ header.tsx # Header with language switcher
β β βββ language-switcher.tsx # Language toggle button
β β βββ ... # Other components
β βββ contexts/ # React contexts
β β βββ language-context.tsx # Language state management
β βββ locales/ # Translation files
β β βββ en.json # English UI translations
β β βββ vi.json # Vietnamese UI translations
β βββ lib/ # Utility functions
β β βββ blog.ts # Blog metadata helpers
β β βββ utils.ts # Common utilities
β βββ styles/ # Global styles
β βββ globals.css # Tailwind CSS imports
βββ mdx-components.tsx # MDX component overrides
βββ .env.local # Environment variables
All UI strings are stored in JSON files (src/locales/en.json and src/locales/vi.json). Components use the useLanguage() hook to access translations:
import { useLanguage } from "@/contexts/language-context";
function MyComponent() {
const { t } = useLanguage();
return <button>{t.nav.home}</button>;
}The LanguageProvider wraps the entire app and provides:
- Current language state (
en|vi) - Language switcher function
- Translations object
- Auto-detection of browser language
- LocalStorage persistence
Blog posts can include content in both languages using tagged components. The content automatically switches based on the selected language without page reload.
# Start development server
pnpm dev
# Build for production
pnpm build
# Start production server
pnpm start
# Lint code
pnpm lint
# Format code
pnpm format
### Single-Language Posts
1. Create a new directory in `src/app/blog/`:
```bash
mkdir src/app/blog/my-first-post-
Add a
page.mdxfile with frontmatter:--- title: "My First Post" description: "A brief description of the post" date: 2024-01-15 --- Your content here...
-
The post will automatically appear on your blog at
/blog/my-first-post/
For posts with Vietnamese and English content, use the bilingual components:
-
Add Vietnamese metadata to frontmatter:
--- title: "My First Post" titleVi: "BΓ i viαΊΏt ΔαΊ§u tiΓͺn cα»§a tΓ΄i" description: "A brief description of the post" descriptionVi: "MΓ΄ tαΊ£ ngαΊ―n gα»n vα» bΓ i viαΊΏt" date: 2024-01-15 ---
-
Import bilingual components:
import { T, P, H2, H3, Bilingual } from "@/components/bilingual-content";
-
Use components for bilingual content:
<H2 en="Introduction" vi="Giα»i thiα»u" /> <P en={<p>This is English content.</p>} vi={<p>ΔΓ’y lΓ nα»i dung tiαΊΏng Viα»t.</p>} /> <Bilingual en={<ul><li>English list item</li></ul>} vi={<ul><li>Mα»₯c danh sΓ‘ch tiαΊΏng Viα»t</li></ul>} />
π Check out the bilingual example post for a complete demonstration!
<T>- Inline bilingual text<P>- Bilingual paragraphs<H2>,<H3>,<H4>- Bilingual headings<Bilingual>- Generic bilingual wrapper for any content<BilingualSection>- For complex multi-element sections
The blog uses Shiki for beautiful code syntax highlighting. Here are the features you can use in your code blocks:
```javascript
console.log("Hello World");
```Use // [!code highlight] comment to highlight the line:
```javascript
function hello() {
console.log("This line is highlighted"); // [!code highlight]
console.log("Normal");
}
```Use // [!code highlight:N] where N is the number of lines to highlight:
```javascript
function hello() {
console.log("Start highlighting"); // [!code highlight:3]
console.log("This is highlighted");
console.log("This is also highlighted");
console.log("Normal");
}
```Use // [!code focus] for a single line or // [!code focus:N] for multiple lines:
```typescript
const [count, setCount] = useState(0); // [!code focus:2]
const [step, setStep] = useState(1);
export function formatDate(date: string) {
return new Date(date).toLocaleDateString("en-US", { // [!code focus:3]
year: "numeric",
month: "short",
});
}
```Use backticks for inline code: `const x = 10;`
MIT License - feel free to use this project for any purpose. Attribution is appreciated!
For issues, feature requests, or questions, please open an issue on GitHub.

