← All projects

Learning project · Frontend R&D

Blog Bits

A small-scale CMS I built to sharpen my frontend, and to test a hunch: that a Shadow-DOM component library plus targeted React islands can replace React where it isn't earning its weight.

Web Components Shadow DOM React (islands) HTMX

Overview

A CMS as a frontend proving ground

Blog Bits started as a way to refine my frontend skills on something real: a small content-management system. But the goal underneath was architectural, to see how far you can get with plain Web Components and the Shadow DOM before reaching for a full framework.

The result is a lightweight UI library that handles the static-and-structural work, with islands of micro React apps dropped in only where genuine interactivity earns it, for example, an embeddable chat widget that can live inside an HTMX page or another Shadow-DOM-based site.

Role Solo: build & research
Theme Islands architecture
Goal Less JS, same UX

Architecture

Server-driven, hydrated only where needed

The CMS renders server-driven HTML enhanced with HTMX. Reusable pieces are Shadow-DOM components with fully scoped styles. Only the interactive pockets (a chat widget, a rich editor) are hydrated as small React islands, so the JavaScript you ship maps to the interactivity you use.

CMS core Content, authoring, server-rendered pages backend
↓
Shadow-DOM UI library Encapsulated, reusable components Web Components
HTMX layer Server-driven interactivity, no SPA HTMX
↓  hydrate selectively  ↓
React islands Chat widget, rich editor, only where needed React

Reach for React last, not first: the library and HTMX carry most of the UI, islands cover the rest.

static HTML  ·  Shadow-DOM components  ·  0 kB JS
💬 chat widget
React island
✎ rich editor
React island
static HTML  ·  server-rendered  ·  0 kB JS

Only the interactive pockets (the accented boxes) become React "islands"; the rest of the page ships zero framework code.

Sample UI experience

Content editor

blogbits.local/admin/posts
Blog Bits
Posts
Pages
Media
Components
Settings
Posts New post
TitleStatus
Shadow DOM, revisited Published
Islands over frameworks Draft
Embedding a chat widget Draft
B I ↳
Editor toolbar · React island
💬
Chat widget · embeddable
</>
Shadow component

A representative view: a mostly server-rendered admin where only the toolbar and chat are React, each isolated in its own island.

Engineering challenges

What was hard, and what I learned

01

Where to draw the hydration boundary

Problem

Islands architecture only pays off if the seam between server-rendered and interactive is drawn well; too coarse and you ship a framework for static content; too fine and the page fragments into dozens of tiny runtimes.

Approach

Make interactivity the unit of decision: a region becomes an island only when it owns real client state, and everything else stays server-rendered.

02

Island state vs. server-driven swaps

Problem

HTMX replaces regions of the DOM from the server, while an island holds its own in-memory state. Reconciling two sources of truth over the same page is the core logical problem.

Approach

Give islands stable identity and a defined lifecycle tied to HTMX's swap events, so ownership of each region is unambiguous at any moment.

03

A stable component contract

Problem

A reusable library lives or dies by its API. Components need a predictable contract (inputs, events, composition) that doesn't leak their internals to consumers.

Approach

Treat each Web Component as a black box with a declared attribute/event interface, so pages compose against a contract rather than an implementation.

04

Keeping React off the critical path

Problem

The whole premise fails if React ends up in the base bundle, then every page pays for a framework most of them don't use.

Approach

Isolate React into per-island bundles loaded only when that island mounts, keeping the default page's dependency graph free of it entirely.