104 lines
3.3 KiB
Markdown
104 lines
3.3 KiB
Markdown
---
|
|
title: Building a Modern Blog with Bun and TypeScript
|
|
date: 2025-10-20
|
|
tags: [Web Development, TypeScript, JavaScript]
|
|
excerpt: A deep dive into building a performant, modern blog using Bun's runtime, TypeScript, and server-side rendering. Learn about the architecture decisions and tradeoffs.
|
|
draft: false
|
|
---
|
|
|
|
# Building a Modern Blog with Bun and TypeScript
|
|
|
|
When I set out to build this blog, I had a few key requirements in mind: fast page loads, minimal JavaScript shipped to the client, and a great developer experience. Here's how I achieved all three.
|
|
|
|
## Why Bun?
|
|
|
|
Bun is an all-in-one JavaScript runtime that's significantly faster than Node.js for many operations. It includes:
|
|
|
|
- A blazing-fast JavaScript/TypeScript runtime
|
|
- Built-in bundler
|
|
- Native TypeScript support
|
|
- Package manager
|
|
- Test runner
|
|
|
|
For this blog, the combination of native TypeScript support and the built-in bundler made Bun an obvious choice.
|
|
|
|
## Architecture Overview
|
|
|
|
The blog uses a hybrid approach:
|
|
|
|
### Server-Side Rendering
|
|
|
|
All HTML is generated on the server using JSX as a templating language. This means:
|
|
|
|
- **Fast initial page loads** - No waiting for JavaScript to download and execute
|
|
- **SEO friendly** - Search engines see fully rendered HTML
|
|
- **Works without JavaScript** - Core functionality doesn't depend on client-side JS
|
|
|
|
### AppShell Pattern
|
|
|
|
The blog implements an "AppShell" pattern where:
|
|
|
|
1. First visit loads the full page with AppShell (sidebar, header, etc.)
|
|
2. Navigation replaces only the `<main>` content
|
|
3. Subsequent requests send a custom header to indicate AppShell is already loaded
|
|
4. Server returns just the content, not the full shell
|
|
|
|
This gives us SPA-like navigation speed with SSR benefits.
|
|
|
|
## Markdown Processing
|
|
|
|
Blog posts are written in Markdown and processed at build time:
|
|
|
|
```typescript
|
|
// Simplified example
|
|
import { marked } from 'marked';
|
|
|
|
const html = marked.parse(markdownContent);
|
|
```
|
|
|
|
The plugin:
|
|
- Scans the content directory
|
|
- Parses frontmatter (title, date, tags, etc.)
|
|
- Converts markdown to HTML
|
|
- Stores everything in SQLite for fast queries
|
|
|
|
## Performance Results
|
|
|
|
The result? Page loads under 128KB including:
|
|
- All HTML
|
|
- All CSS (inlined)
|
|
- Minimal JavaScript for interactivity
|
|
|
|
First contentful paint happens in under 100ms on a fast connection.
|
|
|
|
## Developer Experience
|
|
|
|
With Bun's `--watch` flag, the entire development workflow is seamless:
|
|
|
|
```bash
|
|
bun run --watch ./index.tsx
|
|
```
|
|
|
|
This watches for changes and hot-reloads the server. Combined with the markdown plugin, changing a blog post immediately reflects in the browser.
|
|
|
|
## Lessons Learned
|
|
|
|
### What Worked Well
|
|
|
|
- **JSX for templating** - Familiar, type-safe, and no new syntax to learn
|
|
- **SQLite for search** - Fast, embedded, and perfect for a small blog
|
|
- **Bun's speed** - Development is incredibly fast
|
|
|
|
### What Could Be Better
|
|
|
|
- **Hot reloading** - Would love client-side HMR for styles
|
|
- **Build step** - Currently all processing happens at runtime
|
|
- **TypeScript types** - Virtual modules need proper type definitions
|
|
|
|
## Conclusion
|
|
|
|
Building with Bun has been a great experience. The performance is excellent, and the developer experience is top-notch. If you're building a new project and want to try something modern, I highly recommend giving Bun a shot.
|
|
|
|
The code for this blog is open source - check it out on GitHub!
|
|
|