11-25-2025
Backpack Websites
TL;DR: This is an experimental "backpack website" — tiny enough to fit into a client-side JavaScript file at build time. This approach has considerable drawbacks, but can be worthwhile in certain instances.
Why I Tried This
I want my personal website to feel lightweight and snappy. Think Lightning McQueen: "I am speed." ٩(•̤̀ᵕ•̤́๑)
Most routing setups tie rendering to URL changes or data fetching, which introduces flickers, skeletons, or split-second flashes of old content. For a tiny personal site, this felt unnecessary.
So, I flipped the usual pattern:
- Update local state first: UI re-renders instantly
- Then, URL updates in the background
- URL only drives state on initial load
// Initialize state from URL on first load
const [state, setState] = useState<RoutingState>(() =>
parsePathnameToState(pathname)
)
// Update state when URL changes (browser back/forward)
useEffect(() => {
setState(parsePathnameToState(pathname))
}, [pathname])
The URL becomes a projection of state, not the other way around. It’s fast because everything is already loaded.
Keyboard Navigation Easter Egg
As of v2.8.0, the site has a hidden keyboard navigation feature: Shift + Up/Down to cycle through content.
Navigation wraps around using modulo arithmetic:
const newIndex = (currentIndex + direction + items.length) % items.length
This means you can press Shift+Down repeatedly and cycle through all notes indefinitely, wrapping from the last note back to the first.
Why This Works (And When It Doesn't)
When navigation feels instant, the entire site feels responsive. Readers can stop thinking about "loading" and just explore. Combined with panel width persistence and keyboard shortcuts, you can skim through dozens of notes in seconds without touching the mouse. The interface fades into the background.
This pattern only works because the content is:
- Small (<50 posts for now, ~500KB compressed)
- Static (no user-specific data)
- Same (every visitor sees the same thing)
In other words: a backpack. You carry it all at once, and it’s light enough that you barely notice.
Library Sites vs. Backpack Sites
Meanwhile, other websites are libraries: you request content as you need it:
- News sites (thousands of articles, constantly updating)
- Social networks (user feeds, personalized content)
- E-commerce (product catalogs, inventory changes)
- SaaS apps (user data, real-time collaboration)
- Large blogs (500+ posts is too big to bundle)
These need to fetch data on-demand. There's too much content to bundle upfront, or it changes too frequently, or it's different for each user.
When This Pattern Breaks
Once the backpack gets too heavy, things fall apart:
- Growing to 1,000+ posts → bundles balloon → worse first-load than any router
- Any dynamic data → server fetches re-enter the picture
- Personalization → impossible to pre-bundle
- Real-time features → not feasible
This pattern doesn’t scale. It's a reminder that some things are best left at home, not weighing down your bag.
Why I'm Sharing This
I’m not recommending this as a general approach. This experiment exists to satiate my own curiosity. The web is a playground, and we often learn more by doing, even when our ideas appear questionable. I encourage us to tinker and pursue play in our personal projects. Let our learnings build discretion for future pursuits.
Speed is fun. Constraint teaches us new lessons.
For now, this site fits in a backpack. When that's no longer the case, I’ll return to the drawing board.
Shipped in v2.8.0 on November 24, 2025.