TypeScript HTML generation on the server

Another retro post! It’s 2025, and I’m writing about generating HTML server-side for web apps. After vast experience building SPAs, leading teams to build SPAs, hiring people and training people to build SPAs… it’s clear that SPAs are great for SPA-shaped problems, but are inherently more complex, unnecessarily so, for some other problems.

Hence, for a couple of experimental projects, I’m looking to generate HTML in TypeScript. Speed matters, specifically development speed, So I want an HTML aware typed mechanism, a generic plain text templating system. Here are some of the contenders I found with a few thoughts on each.

KitaJS

This library enables use of TSX on the server with reasonably good typing. It appears to be under ongoing development and in actual use. TSX/JSX is not my favorite syntax, but it is sufficiently good for the problem at hand and readily understandable by many developers and tools. (KitaJS is included in the box in ElysiaJS, by the way.)

Probably the strongest contender.

Thing-Hiccup and Hiccup-html

I somehow made it to March 2025 before being aware of https://thi.ng/ – an incredible mountain of code created primarily by a prolific genius, full of ideas that are like catnip to me.

Thing’s hiccup for TypeScript is a re-implementation of the hiccup package from Clojure. There is an add-on package which has additional HTML-specific type support in the form of factory functions.

The Hiccup data structure style is not quite as approachable as TSX, but it is more concise, which is quite valuable.

This is another strong contender for anyone willing to have their HTML template not look like HTML.

Typed-html

Promising but appears to be abandoned.

bun-tsx-html

This appears to be just an example or a template; I don’t get the impression it’s under any production use anywhere. But it has an interesting approach: Preact-based TSX, then executed on the server to produce HTML – at least if I understood it right.

tsx-dom-ssr

This is one of a collection of TSX/DOM libraries, which can be used for server-side HTML generation. More complete and well-developed than the above, but I’m not completely sure whether it is suited for the purpose I have at hand. I’ll keep an eye on this one, though.

Generic template tools

My search for tools to emit HTML and TypeScript was derailed by an enormous number of generic non-HTML-specific templating tools out there; omitted from this list.

An honorable mention goes to pug.js, which understands HTML, but does not have TypeScript types. I’m slightly sentimental for pug, having used it around 2012-2013 on large AngularJS projects.

Component Sets for Server Side HTML

A retro post! Server-side HTML in 2025! A technology approach I used heavily… 20 years ago.

Some background

At my most recent previous companies, most of our development projects involved a very complex front-end SPA. Angular was our main specialty, though we occasionally used React and others. More recently, at my new firm we acquired an existing substantial React application, so I’m still deep in SPAs.

But some of our product ideas might be more expediently realized with a straightforward server-side HTML architecture, with just a sprinkling of JavaScript powered components, i.e. islands of interactivity, with as little baggage as possible.

Here are my notes from a couple hours of researching suitable available component sets. Feedback is welcome. I’m sure I missed some.

My criteria, which I may not have applied perfectly to this list, were:

  • Cannot rely on React, Angular, etc.
  • Interactivity in the box – not just visuals.
  • Preferably styled with Tailwind.
  • Open source or at least mostly open source.
  • Free is OK, paid is OK if the price is readily visible and measured in hundreds, not thousands.
  • The more complete the component set, the better.
  • Project is alive, continuous ongoing activity in GitHub.

Contenders

I initially wrote these in no particular order and then moved the two strongest contenders to the top, in no particular order.

Shoelace https://shoelace.style/

Shoelace is built as a set of web components/custom elements using the lit-element library an excellent technical choice. It is ready for standalone use, or with four of the most popular SPA frameworks.

Shoelace includes numerous components, With a somewhat minimal but very nice visual style. It uses the “design token” concept for ready customizability.

Shoelace is very impressively polished and complete for an open source project. It was purchased by Font Awesome back in 2022, who is apparently continuing to support its progress.

Strong contender.

FlowBite https://flowbite.com/

FlowBite is a very large collection of components styled with tailwind and with framework-neutral JavaScript-powering interactivity. Also includes integration guides to work with 20 or so frameworks, both SPA and server-side. We can include some more elaborate components than many other collections on this list, including a date range picker, a data table, etc.

FlowBite is “freemium”, with some of the good stuff behind a $299 per-developer cost.

Strong contender.

Alpine.js components https://alpinejs.dev/components

Alpine.js is a popular and to my eye, well-designed JavaScript interactivity add-on library/framework. I think of this as being what the original AngularJS could have been, if it had smaller-scale goals. Alpine.js is popular as a tool to add interactivity to standalone components.

The Alpine.js team also ships a set of already designed components. You can see them in action on the website or pay $99 for a lifetime access to the code and screencasts about the code.

The styling of these components is pretty minimal, which is a good fit for many of the things I build.

This collection appears to have only 9 or so components – A good start, but I will probably choose something that has, quote, everything I think I will need.

Pines UI: https://devdojo.com/pines

Speaking of Alpine.js, Pines UI is a component set with interactivity powered by Alpine.js (and styled with Tailwind).

Pines includes a couple dozen components. There is relatively little activity in the project repository.

Artisan UI: https://x-aui.com/

Artisan appears to be specific to Laravel. I included it here because it looks like a pretty good implementation of the concept, but I’m not a Laravel user.

DaisyUI https://daisyui.com/

Daisy does not match my criteria particularly well. It is only the visual design of components, not the JavaScript, that adds behavior. And it is structured as a Tailwind plugin.

HyperUI https://www.hyperui.dev/

Another one with just visuals, no behavior.

Tailwind UI Block https://tailwindcss.com/plus/ui-blocks

TailwindCSS is an incredible contribution to the software development world – It would be great to support them by choosing their commercial add-on for components.

Unfortunately, offer full, and it’s including behavior if you are using React or Vue, you are on your own without those. So this isn’t a fit for my criteria.

Just AI It?

A couple of years ago, I thought of the kind of component sets listed here as indispensable for building most any real app. But today, you can pull up Bolt.new or v0.dev and ask it to make you a component on the spot. With a little nudging, it could be a web component written with lit-element, or whatever other technical platform fits best into what you are building. While not as tested and production proven as a published component set, it might give you something close enough and optimal for your use case. It’s possible that five years from now, the notion of having to go pick out a component set will be completely obviated with amply good enough code generated on demand at the point of need.

The Outbox – efficient collaboration without interruption

There are lots of ways to get team communication wrong, and I have been guilty of at least several of them!

Email too long. I’m reasonably good at thinking, I think, and reasonably good at writing, and thanks to dictation software, very efficient at turning thinking and writing into a torrent of words. Unfortunately, in a team, there is a considerable cost to creating words and consuming them. I’ve written too many words, too often.

Email too often. Some people are pretty good at juggling a long inbox where each message represents a separate chain of discussion. Many more people are not. I’ve often written many short emails which ignore the truth that humans mostly work in terms of a mostly-single conversation context with each correspondent.

Chat too often. Communicating via a chat tool avoids the many-simultaneous threads-problem, but it bumps into another rough edge of human communication. Even as we use tools which are technically asynchronous, we create distraction (or depending on the hour of the day annoyance). I have both witnessed and been the perpetrator of too-many-chats-too-often.

Long-term / short-term. Some of us have a knack toward thinking at both short-term and long-term levels concurrently all the time. Sometimes this pays off, but more often it is yet another anti-pattern in team communication. A team needs to mostly collaborate and coordinate to get done whatever is on the plate right now.

The Outbox

A few years ago, I stumbled upon a better communication pattern that avoids most of this. I’ve sometimes called it the outbox. An outbox is a place to keep short notes about things you need to communicate to various people; one note/page/file/whatever per person or group. It can be implemented as paper notes, a text file, in Apple Notes or Google Keep, whatever is most convenient. Each person’s output is independent and simple. Whenever some thought comes up, rather than immediately emailing or messaging the person it pertains to, jot a few notes in the outbox.

Then at some reasonable cadence (daily, twice a day, weekly, monthly, etc) discuss it all of it at once.

To keep these discussions sessions maximally helpful, divvy up your outbox between immediate/tactical and strategic ideas. This makes it easy to frequently collaborate on short-term matters, and then less often talk about strategic matters.

This pattern works well with both one-to-one and group communications. You’ll have something useful and planned to say if your team has a regular “stand-up” meeting. You’ll have something useful to talk about next time you have the opportunity to have a discussion with someone. You might even have something great in hand next time you bump into the CEO in an elevator (“great to see you, I’ve been meaning to talk to you about…”).

Even better, a bit of time to percolate in the outbox sometimes gives an idea the chance to go obsolete and be deleted. Maybe you wake up in the morning thinking, “I should talk with Anne about redesigning the company logo”. Should you email about it right now? Well, maybe, but this might be a distraction to Anne who is working on something much more relevant for today. Should you send a chat message to Anne right now? That’s even worse. Instead, jot it in the outbox… perhaps when you meet with Anne at the next interval, you may have realized in between that the logo redesign doesn’t make any sense to pursue tactically. So you discard the idea, never having created a distraction in the first place.

(I’m certainly not claiming this is a new invention. Thinking before you speak is a valuable idea going back forever!)

Nuphy Air75 v2 micro-review

After several years with a traditional full-height mechanical keyboard with clicky blue switches, I no longer want to annoy people in online meetings with the noise, and I missed the flatter profile of Apple’s keyboards. A brief perusal of popular recent reviews led me to the Nuphy Air75 V2 low-profile mechanical keyboard, with the “Moss” higher-spring-weight tactile switches. So far this is a big improvement for daily use. The feel is almost as nice but the sound is greatly reduced. It arrived already set up for Mac use, but with a handful of keycaps to swap out for PC use. It is VIA compatible, which I will use when I eventually get too annoyed with its incorrect layout of the rightmost column of keys. Overall, this is a great choice for a low profile mechanical keyboard at a middle price point.

“Personal News”

Do we still call it “personal news”?

After ~22 years, I’ve left the company I founded in 2001, and the other I cofounded more recently. I’m briefly retired (!), but likely back in the game in 2023.

My contact information at my old work… no longer works. Personal email shown in the ridiculous image below, to foil spammer-scrapers in text.

Tremendous thanks to everyone I’ve worked with in the last couple decades!

Reposurgeon, for high fidelity source control system migration

The best time to blog about something is when it happens. The second best time is when you remember years later that you should have blogged about it. That’s now.

I’ve worked on complex source control system migrations, moving between various systems, most commonly SVN to Git. There are hundreds or thousands of tools and scripts around the Internet suggested for every plausible migration pair; almost all of which don’t even attempt to solve the whole problem.

The closest I seem to solving the whole problem is reposurgeon:

reposurgeon’s web page

reposurgeon source code on Gitlab

The strength of this tool is that it is intended to be scripted. Rather than doing a single-shot conversion, the workflow is:

  1. Attempt the conversion
  2. Study the results
  3. Tweak the conversion script (which can perform extensive and complex changes to the source code history on its way through)
  4. Repeat until approximately perfect

Teams using an old system continue doing so while the migration is worked on. Only once the migration has been perfected, is it time to cut over.

By scripting, I don’t mean that you, the user, must write scripts to do the basics of source control system history migration; that is the job of the tool. Rather, script to patch up the ugly bits of history in an old system during the translation. For example, in a moment of desperation, did somebody once merge a giant change to the mainline, something like rolling back the last three months of development, to try to get a deployable old build? That’s an easy bit of the old history to leave out during a reposurgeon-powered migration.

During migration you can translate usernames, branch names, details buried inside commit messages, and any other aspect you might wish to clean up programmatically. Think of it as a multi-source code system compatible analog of git-rewrite-branch, except for an entire repository, not a branch.

One major downside, as of the last time I used reposurgeon: it operates in memory, so you’ll need enough RAM for the whole source code history. This can typically be accommodated, even on quite large code bases, by temporarily allocating an extremely large compute instance on your cloud provider.