back to articles A terminal-style preview comparing React layers with Lit and native web component boundaries.

> migration.log

React to Lit: what changed when AI joined the migration

A practical note on migrating this portfolio to Lit, and why AI changes how I evaluate framework ergonomics.

April 28, 2026 ยท 6 min read

AI makes it much cheaper to try a technology for real. A migration that used to feel like a weekend-sized bet can now become a short feedback loop: sketch the target architecture, move a vertical slice, add tests, run the browser, and decide with evidence instead of vibes.

That changes how I think about frontend frameworks. A framework with the fastest manual development cycle is not automatically the best choice when AI is helping with the typing, scaffolding, refactors, and repetitive test work. The more interesting question becomes: which stack is easier for humans and AI to inspect, reason about, and safely change together?

Why Lit became interesting here

This portfolio started from a more React-shaped mental model. Moving to Lit made the code feel closer to the platform again: custom elements, explicit properties, native events, lifecycle methods, and templates that read like HTML with JavaScript expressions. There is less framework ceremony between the component and the browser.

I have also always defended a slightly unpopular React opinion: class components were often easier to understand than many modern function components, especially for people starting out as developers, mostly because their lifecycle was explicit. You could see where setup, updates, and teardown belonged. Custom Elements give me a similar kind of clarity: connectedCallback, disconnectedCallback, and explicit update hooks make behavior easier to place.

There is also personal history here. Between roughly 2015 and 2019, a meaningful part of my career involved Custom Elements, and they felt like magic at the time. Since those days, I kept the intuition that Custom Elements could eventually become a framework-agnostic, future-proof way to build UI. That is still not universally true, but it still feels directionally true enough to revisit.

So the points below are specifically about using Lit in this portfolio migration, not a universal claim about every Custom Elements setup.

  • The component boundary is a real custom element, not only a framework convention.
  • Lifecycle is explicit: connected, disconnected, first update, reactive updates.
  • Events and attributes stay close to browser primitives, which makes behavior easier to trace.
  • The final output feels portable: a page made of web components instead of an app that only exists inside one renderer.

Where AI seemed to navigate better

My strongest impression during the migration was that explicit code gives AI fewer hiding places. React codebases can be beautifully simple, but they can also accumulate layers: hooks over hooks, provider trees, memoization rules, router conventions, data libraries, framework-specific rendering rules, and build-time behavior. None of that is bad by default, but it can make the actual runtime shape harder to see.

With Lit, a lot of the important questions are visible in one place. What properties does the element receive? What event does it dispatch? What lifecycle hook owns the listener? What HTML does it render? The answers are usually plain class members, methods, and templates. That explicitness helped the AI make smaller, more local changes, and it made my review faster because the browser model was still recognizable.

The good parts we found

  • The public boundary became explicit as properties, attributes, and events, while the actual API clarity still came from component design rather than Lit by itself.
  • Custom elements made the page structure feel more honest and inspectable.
  • The native lifecycle made scroll listeners, header state, and route behavior straightforward to place.
  • The app now feels closer to how old web pages used to be built: HTML first, a little JavaScript around behavior, and components that still look like the platform.

The tradeoffs are real

The biggest downside was developer tooling around templates. JSX still has a very strong TypeScript story: autocomplete, prop checking, editor feedback, and ecosystem conventions are excellent. Lit can get closer with TypeScript declarations, Lit Analyzer, and strict template checks, but writing custom elements inside html templates still does not feel as effortless as JSX.

  • Template type checking required extra setup instead of being the default experience.
  • Property binding, attribute binding, and event binding need more attention from the developer.
  • Testing web components is pleasant once configured, but the setup is less familiar than React Testing Library.
  • React has a much larger ecosystem for product work, especially around routing, forms, animation, and app frameworks.

The conclusion for now

I am not reading this migration as React versus Lit. React is still an incredible default for many products. The lesson is more specific: AI reduces the premium on frameworks that mostly optimize typing speed, and increases the value of code that is explicit, inspectable, close to web standards, and easy to verify.

For this portfolio, Lit fits that direction. It gives me a smaller surface area, real component boundaries, and a codebase that an AI assistant can move through without needing to reconstruct too many invisible layers first. That feels like a good foundation for experimenting.