Webtech is always changing. The times of software sitting on relatievly the same stack for ten years are gone. You’ll be lucky if the frontend you write today lasts without major user interface churn for two years, and incredibly fortunate if the ui framework doesn’t lose developer popularity or crawl to a slog in five years. The backend ecosystem is more stable, but you might say it is just as off the Rails as frontend development.
Here’s my recommendations on all things JavaScript Webtech.
Languages
We’re talking about JavaScript webtech here. If you skipped over the CoffeeScript era, that’s great. For now, the languages we really pay attention to are TypeScript and JavaScript itself.
TypeScript
If you can start a new project today with TypeScript, using TypeScript is easier. Migrating a legacy codebase is a challenge. The initial warmup time learning TypeScript feels steep, but that’s OK. You don’t need to use recursive types, you don’t need crazy interfaces. Just have it setup. Use types where they feel like inline documentation. Object shapes, arguments, and maybe that’s enough to get started.
If the tooling can support TypeScript, turn it on.
With TypeScript, you should turn on strict
. It will cover so many edge cases for truthy, null and undefined checks. You will save time with this on, becuase you will avoid rewriting dozens of files.
🍌 As a cautionary plea, it’s easy to get into the groove of TypeScript feel like you’re empowered to create every typeful concoction you can think of. Resist the urge.
JavaScript
At the language level, there are occasional bursts of progress. ES6 kicked things off in 2015. Roughly eight years later, the cadence of progress is still present though sometimes it feels slower than perhaps other ecosystems (thinking about Java, surprisingly, and PHP 8.x, incidentally). Nevertheless, language churn for change’s sake is wasteful.
If you can only write plain old JavaScript, that’s fine. You will need diligence though. You will need to have additional runtime validation to ensure shapes and structures are what you say they are. You will need many more tests!
Modern Bundlers / Transpilers / Compilers
Babel and Webpack, they’re our good friends. But they’re slow now.
swc
I used to promote swc
regularly. I hoped that being backed by Vercel, it would have industry wide adoption. I think there’s a case to be made that the majority of adoption its earned so far is directly because of Next. Outside of the knobs and levers that Next tunes ever so right, getting swc to work with plain hopefully modern plain Node projects is shockingly difficult. On the client side, there’s not really a starter kit that brings along for the ride all the settings you’d ideally want. Plus, it’s not really a bundler, it’s more of a compiler. Undoubtedly, it’s fast. But that one testing issue gave me a bad feeling.
esbuild
While I had not as fervently promoted esbuild
, I can share good news! You’ll likely use esbuild on the frontend when you use Vite for your frontend single page app.
typescript & tsc
What’s wrong with typescript
and tsc
? Nothing, actually. For years I had tried to innovate in place by testing in production wacky contraptions like swc. New tools are great, yet choosing boring tools has the advantage of accumulated wisdom and expertise behind it.
React and friends
It’s a React world out there. There are still other options though. Svelte, Solid, Vue. We will not mention Angular. Alpine, htmx probably a horse’s best friend and probably dozens more. Oh, plus jQuery.
If you’re reading this, you’re likely going to use React by default.
But, what about the others?
- Angular
- allegedly enterprises use Angular but luckly I do not work in such an enterprise
- Svelte
- I saw first hand a project repeatedly upgrade through version 1, 2 and then 3, not quite smooth, not quite a blocker; certainly less painful than AngularJS to Angular 2
- Solid
- I like that it keeps JSX around, being one of the strongest points of React
- I like that it optimizes renders by not just re-rendering on every rolling and cascading state change
- I think the hardest aspect of Solid today is, well, its ecosystem is small
- Preact
- Everyone loves to tell me about awesome it is that “you know this is so basic we could use Preact instead” but then you want to do one additional realisitic thing poof, the more Preact
- Preact appears from the outside to be 80% compatible, which in userland, gets you far enough, but pulling in libraries that use more complex React flavored internals, well, you might end up out of luck
- Vue
- I worked with Vue 2 extensively in the 2017-2018 era, prior to React’s hooks implementation
- At the time, I liked Vue, but I increasingly disliked Single File Components (SFCs) and found the allure of “it’s just another function in the same file” that React offered (class Components for statefulness, functional components that were pure for everything else)
There are dozens of these things. Honestly, I do not know all of them. PRs welcome.
React gets my pick today because…
- it’s incredibly popular
- it’s in demand
- it has a huge practioner base
- it has a wide, fairly thriving ecosystem
- the middle 80% React is conceptually fine, while the outlying edges can be a bit rough
I would be remiss to say it’s all unicorns and rainbows with React though.
Modern React “Frameworks” for “Apps”
The React ecosystem is the largest frontend “framework” developer ecosystem for “apps”.
What is a “framework” and and what is an “app” anyway?
Single Page Application (SPA)
There’s a lot of debate about single page apps right now. With the advent of the “new” react.dev docs, and in particular the “Start a New React Project” page, there’s even more confusion and consternation about SPAs, MPAs and Hybrid apps.
There are all sorts of reasons why an SPA might have poor performance, directly impacting user expereince. Slow connections, high latencies, huge bundles, slow first paints, the list goes on.
For the most part, I think that for commodity apps like adminitistative portals, back-of-house configuration management tools, back-office management tools, data dashboards, these signle page apps are fine.
Given that, I think there are situations and circumstances where a Single Page App is not what you should reach for:
- When you need your site and pages on Search Engine Result Pages (SERPs)
- You’ll never get an SPA properly decorated in on the SERPs and properly ranked; not enough content, not enough meta tag control, no matter what they tell you about Googlebot crawling SPAs
- If you’re thinking about e-commerce at all, you cannot use an SPA
- When the majority of your pages are full of static content
- The two other scenarios cover this; server side rendering / hybrid apps, and static site generation
- You need robust redirection
- When you cannot make portions of your frontend code effectively public
- This almost never happens; put special secret sauce business logic behind APIs where they belong and your treasure will stay software
CRA
create-react-app
is dead. What will happen to create-react-app
?
Should port your application away? Yes.
What should you port your application to? Vite.
Vite
Vite is your best bet for an SPA. It is the alternative to create-react-app
.
It’s fast because it uses esbuild
or optionally swc
as of v4. Its community is thriving, its configuration out of the box is great, it supports TypeScript, it has a third of the dependencies of CRA. There are some downsides of course. Jest is wacky with ESM, so Vitest is a critical alternative testing tool, but the porting process is tedious although not laborious.
Aside from TypeScript and some other high level configuration, there’s not much actual application code that could break if your previous CRA configuration was truly vanilla. The most trouble I’ve had porting apps from CRA to Vite has been ancient dependencies that have no awareness of the CJS vs ESM distinction.
Here’s the curiosity though: the new react.dev installation guide goes deep into framework-ful frameworks. It mysteriously burries what would have been the nearest viable alternative to create-react-app in a secondary section. Now, below I also go deep into these hybrid apps but I think it is irresponsible to discard over ten years of momentum when your 2018-era feature set has just been documented.
Webpack
You might say: “hey, I have such special needs that I must have a customized webpack option.”
You’re wrong. You do not.
To be honest, this Webpack section is so awkward here now. It’s difficult to even remember a time when I had to poke that thing with a stick in fear of it trying to bite the hand that feeds it. Everything changes!
Server Side Rendered / Hybrid Apps
In 2021, everyone’s svelte friend Rich Harris presented the talk “Have Single-Page Apps Ruined the Web? | Transitional Apps”. In this talk, Rich discribed the pros and cons of Single Page Apps and Multi-Page Apps and tried coining a new term called Transitional Apps. I think the term fell short of popular adoption. Usually I hear this class of app as a hyrbid app. That’s fine, but I feel like we had hybrid apps for a long time, where we were doing funny tricks. This generation of hybrid apps are much more integrated and perhaps deserve that new name now.
Get your pages listed on SERPs! Get indexed today!
Rolling your own SSR with React today is not easy. Sure, you can do it, but it will be so customized, unrepeatable and unknowable. This is a Dunning Kruger decision. If you’re asking if you should build your own framework that straddles server side and client side, the answer is no. If you’re asking if you should build your own framework because you have expereince with their deficiencies, have articulated needs for their affordances and have explored many available options, then perhaps you could consider it. Or use Next?
Next
Next and Remix are the transitional hybrid options here today.
Next has been around for a while. It has some quirks, most of them obligations of its transitional hybrid nature. Not all of these quirks are bad, but they’re things to think about, and things to be unfamiliar with if you have only worked on JSPs or CRAs (one of these is not like the other, almost by definition).
- routing is based on the filesystem route
- components are not colocated with their pages
- there are weird magical functions that run if they’re defined (
getStaticProps
,getServerSideProps
, etc) - i18n is a rabbit hole
- authentication and authorization is a deeper rabbit hole
- middleware gets a funny runtime
- middleware gets weird behind a cdn
- app router; dozens of files
- mixing execution paradigms
- unknown patterns for mandatory implementation details
These quirks only grew with the App Router from Next. This new mode also enabled React Server Components (RSC) with Next 13 in the app/
directory. Next 14 enabled server actions, but do not get me started right now.
The internals of Next are solid. It runs swc
under its hood, so its fast enough. If you need some prerendered pages, some server side rendered pages (on demand) and some SPA-like functionality, then Next is great. It does it all.
My hestiation is split between “can-my-team-figure-it-out” and the use case. The split between hybrid and spa is still pertinent. Next gives you great powers, but with great powers comes great responsbility, and in this case, new downsides to balance in your compromise.
- 🙉 Do you really need your pages indexed? Not really, especially if they’re locked behind corporate firewalls or are entirely authenticated experienced.
- 🙈 Do you have a centralized data source (instead of distributed)? With data centralization, running at the edge will not be a major latency improvement.
- 🙊 Do you really have developers that are ready to solve entirely new types of problems that come up with state shared between the server, the edge and the browser? You might, you might not. But it’s not free. It’s bizarre to question my teams if they can use Next or not. Sure, of course they can. But they have had 6 years of full time create-react-app flavored development. It’s just tough to justify.
In 2023, being a react.dev installation guide framework suggestion, surely Next is best? Well, React Server Components came around at the same time as the Next 13.4 release, and along with that, the official recommendation that Next is the framework for React. Now there’s this dichotomy between React Server Components (RSC) and Client Components, and a whole new world of integration and Mindshare Interia™ to overcome because of this flip flop. Finally, there is documented discourse and decided displeasure for having for RSC. It all still feels very beta to me (like the classical definition of beta, released, but unstable). I spent an afternoon tinkering with RSCs but left it at that. It’s not ready and I am not ready.
In Fall 2023, I attended React Summit US hosted in New Jersey. There’s a particularly good panel discussion about Hybrid Apps powered by React Server Components that you should watch. During the Q&A section, I asked the hardest question: for those commodity apps, should we go through this RSC effort? To be honest, I think none of the answers presented were compelling, but I was truthfully looking for validation and hoping for someone to plainly say, “no, this is not ready yet.” Andrew Clark did say that in two years, we’ll look back at this time as the beginning. There’s a lot of Mindshare Interia™ to overcome, patterns and pacakges to build between now and then.
Dan Abramov writes about “The Two Reacts”, which is sort of a Socratic method hypothetical scenario to help understand the Client and “Server” “Serialized” flavors of React.
Dan’s “The Two Reacts” Excerpt
But in practice, the real “formula” is closer to UI = f(data, state). If you had no data or no state, it would generalize to those cases. But ideally, I’d prefer my programming paradigm to be able to handle both cases without having to pick another abstraction, and I know at least a few of you would like that too.
The problem to solve, then, is how to split our “f” across two very different programming environments. Is that even possible? Recall we’re not talking about some actual function called f—here, f represents all our components.
Is there some way we could split components between your computer and mine in a way that preserves what’s great about React? Could we combine and nest components from two different environments? How would that work?
How should that work?
Dan’s positioning this for a follow up post where these Two Reacts are combined back into one React. I’ll update this section with an excerpt when that comes along.
There’s increasing discord regarding the flat-fallen hype sundering of the React ecosystem:
- Cassidoo - Annoyed at React
- Matteo Frana - React, where are you going?
- Zac Skalko - The React Empire
It’s going to be a while for the dust to settle in this part of the ecosystem. Look, nobody is forcing you to use this stuff now. Do what works for right now, until it stops working. Then react.
Remix
Other than Next, there’s Remix.
Remix is still on my watch list, but there it has remained since 2022. Hydrogen is some ssort of Remix too, and Remix joined Shopify, and Kent left Remix. There’s so much going, I can hardly keep up!
In Early 2024, Remix 2.5.0 released an “(Unstable)” SPA Mode feature. It’s bridging the gap between React Router v6 era static website single page apps and Hybrid Apps. It’s early days for this, but if it gains any traction it will be promising. This is the committement to backwards compatability that I like to see.
Remix demos well in videos and tutorials. I am waiting for more community usage though.
Static Site Generated
Brochure-ware at its finest. Or maybe a relatively stable marketing page?
If you have markdown, or some relatively narrow set of pages with data sourced from an API or local file, static site generation works. This can break down when you need interactive editing (for that go back to Next), or when you have so many pages rebuilding them is wasteful.
- 🔴 Not recommended: Gatsby: it feels a lot like JavaScript flavored WordPress
- the GraphQL model is too much work
- the mix of esm and cjs files is weird
- the deployment tech is optimized only for their first party deployment service
- 🤔 it is recommended in the new react.dev “Start a New React Project” docs, surprisingly
- 💀 []“enterprise” rely on Gatsby so it’s not “dead”](https://github.com/gatsbyjs/gatsby/issues/38696); it’s dead
- 🍊 Cautiously Recommended: Next: it works with everything, including static sites!
- My annual card site is built with Next and uses no SSR features
- Next shines in this regard when coupled with its parent Vercel service or a similar edge provider
- Static exports are available for a truly S3 class experience; but and there’s always a but, I think Next is slowly shaking this capability out of the framework having adopted this server centric approach
- ✅ Recommended: Astro: it works with everything, even React!
.astro
files use jsx syntax, but act more like one of those new React Server Components in that they only render HTML at the end, and bring no additional JavaScript payload down- seriously, incredible work was done to ensure interop between React, Vue and others
- react usage would bring in the react bundle, but its on demand and situational
- uses
vite
under its hood, which in turn usesesbuild
under its hood; new tools all the way down - successor development team to snowpack; well, hopefully it does better than that
- island architecture is awesome; only load if it’s in the viewport and only this small chunk is impacted
- you can even use plain js, with the bundler, and modern tooling, and packages… no framework at all
As a testement to Astro, it’s used in various scheduled static deployments and internal sites.
React flavored State Management
Redux is a lot. Sagas are too much. Redux Toolkit makes it better, but it is still a lot.
Maybe you don’t need an enterprise service bus on the frontend.
Consider:
Tree-Local-State
Components might have state, subtrees might have state, and your entire app might have state.
For subtree, or what I’ve liked calling, tree-local-state, you can do a lot with context.
- Context often has performance issues; you put a context at the top of your entire app, you shove stuff in like Redux and your app gets slower and slower because your entire tree rerenders
- Context is fine if you use it as a read/write 80/20 store; auth states might be fine in a Context as long as you read and you don’t write ever again
- Really consider Context as an escape hatch for props drilling
Server & Client Shared State
react-query
is usually more than enough for fetching server side state. It provides for a great developer experience, and lends itself out of the box to a pretty decent user expereince too. I can’t recommend React Query enough. Tanner is cool, I’ll never call it TanStack though.
Complex State
zustand
is a great option for more complex state, SPA-style cross page data propagation use cases
- you can combine this with
immer
for better ergonomics - you should justify the bundle size impact
- read more about zustand: a middleground
Like ice cream, there are plenty of state management flavors in this space. In addition to zustand, jotai and valtio are both popular too.
🍌 If you have extraordinarily complex state, Redux with Redux Toolkit has its place. It’s a slippery slope and once you go down this route and will have a hard time coming back. React accordingly.
Offline
A few years ago, I was involved with multiple projects that insisted client side state persistence for offline support. Despite all these years of development and industrial growth, I still think offline webapps are a very poor experience. Users have no idea what’s going on. Engineers barely have any better tools to make it a decent experience. In general, if offline is where your web app will be used, give all that PWA business a try and enjoy the fruitless pursuit of the perfect sw.js
, but also seriously consider a real app or being dependent on generally available Internet access.
🍌 I am not exaggerating about my stance on server workers. Doing any caching logic inside of service worker unless its core to your business is destined to fail and lead your customers into undebuggable situations.
In this rare situations, I have reached for redux-offline but surely there’s something better now. Like building a real native app.
Server Side / API
With something like Rails, have your frontend and your backend too, just like the cake. Even though the JavaScript ecosystem is uniquely suited towards driving the frontend and backend simultaneously language-wise, the interop bridge between is still clearly in active development. It was not built this way from the beginning, and it shows. Just look at Next and Remix for those examples. Even if you consider those a “backend”, they’re not the whole story, they’re just best friends. There’s no Rails. There’s no artisanal JavaScript backend.
If anything, Next and Remix are the fabled Backends for Frontends dream fufilled, but instead of being an composition API, its composing data from API into UI. What about the actual backend, where your database lives and you interop with your own API friends?
Nest
❌ Not Recommended
Historically, I have promoted Nest as a viable option for an actual backend.
For Nest, I have unresolved issues, even after years of trying:
- Annotations are half baked, not spec, and feel ergonomically odd in the language
- They’re great when they’re magic you consume, but if you need to go any deeper, they’re just so obtuse, and it certainly does not help that TypeScript and JavaScript have no runtime reflection, so its just an illusion
- Silly defaults
- Why was
strict
off?
- Why was
- The Nest service container is kind of weird, it uses, well ironically now that I think about it, nested modules that isolate services into logical units
- Dependency Injection (DI) isn’t for everyone, though it has its uses
- With Spring, it all lived in one service contain freely, no modules required
- The register/bootstrap process, unlike Laravel, isn’t clear and it’s not well described
- TypeORM, the ORM that was highly promoted in the first party docs, was effectively abandoned
- Integration with npm ecosystem tools is often painful
- There are adapters for many popular libraries and patterns, but not everything
- Dependencies… so many… dependencies
- The more dependency surface area you have, the more time you spend auditing your security than building something useful
- Nest still ships without a modern compiler backend (esbuild, swc) making it slower than it needs to be
- Nest ships without new options for ESM
Arguably it is valuable in a larger team setting because it provides much needed structure. Files go here, they’re named this way, there are interfaces you can recognize and research.
I think you can use it, but you need to make the choice. The hard choice.
Express
Yep, good old Express. Express 5 will never come out of beta. It uses an API design that’s from the early days of Node and we’re stuck with it for now.
Popular middleware like Passport are beginning to age out, which is great, because the momentum that express brought will age out too and something else can come along.
Did you wonder why Express just sits there? Koa was built by the same folks, but it never really caught on.
Hono
Hono feels like a backend router baked in 2023 and not in 2010. The API is still Express like, but it has no external dependencies, it works in various runtimes, it has a trimmed internal surface area. It’s meeting the criteria of Express without its legacy bagage.
I am currently trialing Hono as our Node backend routing option. We’ll see how it goes, but it’s initially promising.
Elysia
Is there a bun in the oven? Elysia is a great hypermodern option. I have experimented with it, but in an early version before its stable release. I think being tied to Bun is both bane and boon; it’s much more limited for slow movers (enterprise) but for those jumping into Bun, there’s something fresh to use right now.
Fastify
Fastify is worth looking into, but I am not interested.
Non-JavaScript backends
I think JavaScript earns a rightful place in your backend stack, but only up to some threshold. It is not perfect by any means. If your team has more familiarity with another stack, consider that. But know that the vision of “backend for frontend” persist and it is coming sooner than later.
- Go
- Only recent have I become re-interested in Go, partly because I needed to write non-Node code for a project, and also for it to align with an already existing Node code suite and a team that writes primarily in Go
- It’s very fast, it’s all very literal, it’s all very non-DRY (which is surprisingly a feature now)
- It still feels like I am writing BASIC in Python upside down because of the lack of filter/map/reduce features languages (even Java…) have had for years
- Java
- Ok, I know what you are thinking, and surely I cannot be serious
- Java 17/21 is actually capable of providing a modern language feature set, even richer, than dare I say, Go?
- Spring Boot might not be everyone’s favorite but it is really quite a good target for long term software since someone will know something about it (which is the same argument used for React above)
Package Managers
npm
npm
is never going away. But that does not mean you should use it. For me, my situation lends itself towards better tooling.
- legacy of poorer performance is still remarkably present
- lagging feature set (only relatively recently did
npm
gainoverrides
)
Because I jump around project so often, installing, reinstalling, with overlapping dependencies, npm
is not my preference. I still feel its unreliable too.
yarn
yarn
is now just in such a sad state. yarn
, as in yarn classic or Yarn 1, will be stuck in time as its codebase was abandoned without any hope of shadowing the same interface with its descendant rewrites. yarn
, as in yarn berry or Yarn 2, 3, 4… Frankly, I am speechless about the whole thing. It’s almost entirely AngularJS → Angular
again. For our work, split between frontend and backend, in an ecosystem dominated by yarn classic and npm
, the changes to behavior and ecosystem interaction is so drastic its nearly unusable.
I am so sad about this prospect about giving up yarn
. It’s in every tutorial out there. And now it will fade into obscurity.
A deeper look at yarn berry weirdness
Here’s two examples of weirdness from berry:
-
yarn install --production
When you build a docker image, you build it with the minimal code. No development dependencies, no test files, no unncessary configuration files, and so it goes.
Well, somehow berry managed to mess that up by taking that command making it way weirder by requiring a completely new command:
# good yarn install --production # huh?! yarn workspaces focus --all --production
Previously, I thought I had read here that even this feature, which is much more complicated than it used to be, required an accompanying plugin to even work. Maybe in the latest version that was just rolled into the mainline. We’ll never know.
-
.yarnrc.yml
If you have
.npmrc
files, you might use them for configuration of npm, and likely setting details for private package mirrors. Cool, way to go!But if you thought those would work with berry, by default, what — are you crazy? No way! Let’s introduce an entirely new file and ignore the old
.npmrc
we all have.
pnpm
pnpm
is now my preference. I feel like it’s a successor to npm
in name but functionally an evolution of goodness of yarn
(as in, yarn classic).
It has all the things I want:
- fast install times because of a global cache
- a sensible api / cli interface
- features for removing dev-deps without monkeying with module folders
- lockfile upgrades
- reads and respects .npmrc
- auto-resolving conflicts
- … and the list goes on …
My only pertinent criticism of pnpm
is about using it in a docker image based build system, since pnpm
is not enabled by default in the docker hub node images. Despite that, it’s actually fine, if not just for a tiny bit more noise.
#
# install dependencies
#
- name: install
image: node:20-alpine
commands:
- corepack enable && corepack prepare pnpm@latest --activate
- pnpm install --frozen-lockfile
Oh, ok, fine. One more. I’ll never get used to typing it. I’ve used yarn
for years.
I know my new found appreciation for pnpm
will be a hard sell, but that’s ok. Try it, see if you like it.
Validation
Validation is often overlooked and undercooked.
- 🟠 ajv
- pro: json schema oriented
- pro: TypeScript support
- con: limited support for imperative validation
- con: moving target
- con: verbose
- 🟠 yup
- pro: easy to get started
- con: mixed support for complex validations (or imperative validation)
- con: major type-rewrite in recent history
- ✅ zod
- pro: easy to get started (similar to yup)
- pro: there are adapters to convert zod schemas into json schemas
Schema validation is not the same as business logic validation. For example, if you have a schema {count: number}
, that’s pretty easy to validate for all of these libraries. Suppose you need to validate the number is in a range. Also easy. What if the number has to in a fixed range, and also related to a value in a database? That feels like it is outside the scope of schema validation, but having schema validation that could do this, or share its error structure with you is worth it.
For now, I recommend zod.
XMLHttpRequest, XHR, fetch
For backend apps, I recommend using fetch
right out of the Node 18+ box. You should be on Node 20 now.
For hybrid apps (with Next), I recommend fetch
. Out of the box, Next will provide fetch
in its middleware and server side. fetch
will work in the browser on the frontend. Note that Next does tinker extend with the fetch
function a bit to add their own special sauce.
For an SPA, I recommend axios. I know, this breaks the pattern. Axios has been a go to library for years. Likewise, for many years it felt like it was abandoned, languishing in a 0.x version. Now that it is 1.x, I feel better about it. I hope a 2.x variant comes around with fetch
internals, now that browsers and even node have widely implemented fetch. As a close second, fetch
from the browser runtime is also just fine, but I enjoy the additional capabilities axios provides.
Form management
Back in my Vue days, I used VeeValidate. We were going into a project using React Native though, so no more VeeValidate. But how could I make form validation simple and smooth just like that? In Class-based React code? I did some hacking over winter break and I came up with some kludges but nothing substantial. Good thing too. I found Formik shortly thereafter.
Forms are the worst part of any webapp.
- 🟢 react-hook-form
- by default, using uncontrolled inputs, react hook form manages to do all the useful form management things and keep performance much higher because the component tree isn’t always re-rendering
- react-hook-form also has escape hatches for controlled inputs, letting you create synthetic form inputs as you need (like, for example, a non-native date picker)
- 🍌 using react-hook-form for controlled inputs is fine, but too much of a good thing is bad, and of course if you have hundreds of controlled inputs updating concurrently, there’s bound to be lag; for example, I was recently working with a team that had roughly 1400 controlled form elements mounted which caused lag
- 🟠 Formik
- I haven’t used Formik in a while but I think it’s still widely used and most likely fine for the majority of forms
- 🟠 [TanStack Form]https://tanstack.com/form/latest)
- It’s a Version 0 so it’s definitely not ready yet, but it’s interesting either way
Hey, but, did you know you can use native browser validation too?
UI libraries
You knew this was coming.
Tailwind is the only UI library you need. Then you need creativity and time.
- 🟠 Tailwind
- What, putting an orange caution circle on my own recommendation? Who’s better to provide constructive criticism? You should always distrust your favorites.
- Pros:
- atomic design at its finest purest form
- co-locates styles and markup structure and presentation logic together
- makes AHA style programming easy (neither, DRY, nor WET; “prefer duplication over the wrong abstraction” or “Optimize for change first”)
- The first major con, if you call it that, is that its still difficult to package and distribute components styled with Tailwind to other projects; approaches like shadcn ui and catalyst forgoe the usual library approach and instead copy these basic components into your own project - reusability is still tricky
- The second minor con, if you call it that, is that paired with React and TypeScript, when you do need to make an abstraction, there’s been little guidance on how to actually do this in a safe and sane way. Perhaps with some more practice, this will get easier, and hopefully a first party adoption of tailwind-merge
- The third patch con, if you call it that, is that Tailwind requires a mindset shift if you’re used to something else. I’ve been on the receiving end of disdain for liking and using Tailwind. It’s come up time and time again on Hacker News, always entertaining either way. Give it a try.
- 🙅♂️ MUI
- If you don’t like MUI out of the box and do not want to use it exactly it comes out of the box, maybe barring some basic color changes, then… do not use MUI
- It is clunky beyond belief, born in a time before flex and grid became standard
- It is clunky beyond belief, born in a time before TypeScript was effectively required in and library toolkit
There are others in this space, and they may fall somewhere between Tailwind and MUI.
Wow, sorry, I accidentially slipped and wound up in my visually stunning and simple to maintain ivory tower. I accept the purpose and perhaps even some out of the box advantages of MUI. That does not mean I like it.
What’s next?
What should I write about next in the ecosystem?
Follow me on Mastodon @ryanmr@mastodon.cloud.
Follow me on Twitter @ryanmr.