A React Developer’s First Take on Solid
Hello, {yelling()}!
is the trickiest part, because JSX in Solid works slightly different than in React. Rather than just rendering this immediately, Solid remembers that the text of thiselement depends on the result of the yelling function. Then, when that function’s dependencies change, Solid figures out what actually changed and updates the DOM again. setName(event.currentTarget.value); works roughly the same as in React. Calling it updates the value of the name signal, which causes Solid to re-evaluate anything that depends on it — in this case, the yelling function and the text inside the
tag. Essentially, what the component does is set up a pipeline from createSignal to the DOM. When you call render, Solid remembers how that whole pipeline works. Then, when the value of the signal changes — in this case, by calling setName — Solid runs only that pipeline, not bothering with any other pipelines that it knows haven’t changed. I know that’s kinda hard to visualize. If you’re interested, Ryan has a good talk that goes in-depth into how this works. In practical terms, though, it doesn’t have a huge impact on the developer experience. There are a couple gotchas to keep in mind, but for the most part you can write your code as if you were building a React app and it just… works. Okay, so what are the gotchas? One, no destructuring props. Solid does some fancy Proxy stuff to track property access that gets messed up if you do. Two, because component functions only run once, you need to wrap any derived values in their own functions. And three (or I guess two and a half) you use special components for looping and showing/hiding, rather than maps and ternaries. Again, these are pretty small drawbacks. They’re kind of like React’s Rules of Hooks, in that you might initially think it’s weird to need to call functions in the same order each time until you realize that you already write most of your code like this anyway. Let’s look at one more example — the infamous counter app: Again, very similar to React. createSignal plays the role of useState, and the API is almost identical except that the "state" variable count is a function that returns the current value. You’ll also notice I put a console.log inside the Counter component — once in the body itself, and once in createEffect, which is Solid’s version of useEffect. Solid knows that there’s a "pipeline" between that count variable and the
tag, it and updates the DOM automatically when the value changes. It also knows that there’s another pipeline that from count to createEffect, so it reruns the effect. Here’s a link to a live version of this code, if it helps. If you run it and click the button a few times, you’ll see that the “component” log doesn’t happen when you click; these functions are, indeed, only called once. The "effect" log, on the other hand, happens every time the count is updated. You should see something like this in the console: All this is nice, but what’s the advantage over React? For one, it’s small, although that makes less of a difference the larger your app gets. There are marginal developer experience improvements, such as not having to track createEffect dependencies. And finally: because it’s only updating the exact things that change, it’s also really fast. Solid Start If Solid is React, then Solid Start is Next.js. The Solid Start website cautions that it’s in beta, and they’re definitely not lying. But it also has some awesome features that seem genuinely transformative. First, it has the file system routing that’s become bog standard amongst frontend frameworks these days. The default styling solution is CSS modules. It supports both server-side rendering and client-side rendering. There are provided components for managing head tags. Let’s talk about loading data. Traditionally, single-page apps are split into two layers: a JavaScript frontend that runs in the browser, and a separate REST(ish) API that runs on the server, communicating with the client using JSON. Lately, frameworks like Next.js have begun combining the two, allowing developers to create “API routes” in the same codebase as their user-facing components. Solid Start’s answer to this is route data — a Remix-inspired feature that makes it super easy to load data, both on the server and client. If you export a function called routeData from a page, calling the hook useRouteData within the component will give you the data you returned from routeData. The secret sauce is that Solid takes care of wiring up the HTTP request and getting the response back to your component: That’s for reading data. For writing, there are route actions. They’re pretty similar, except you call them from within your component code and wire them up to your JSX, like a hook. They even return a
component, which makes it super easy to create progressively enhanced HTML forms that work without JavaScript: I won’t bury the lede: it’s pretty magical. Solid Start even includes functions createServerData$ and createServerAction$ for operations that should only happen on the server. You can make database queries in the same file as your components, and Solid will not only render the markup on the server when the page first loads, but also request new data and update the DOM whenever any of the parameters change. It’s way faster and less boilerplate-y than manually writing new REST endpoints or GraphQL resolvers. Frankly, I’m a little skeptical of how well this scales. As apps grow, they tend to require additional complexity. Things like authorization and rate limiting. Although the Solid Start documentation does include an example of how to implement sessions, my gut says that bigger apps will still benefit from a separate “traditional” API. But for starting out quickly, or for apps that are likely to remain small, such as prototypes or side projects, this is perfect. Solid Start does offer the API routes found in Next.js as well, though. In files within the routes directory, you can export functions named after HTTP verbs to respond to requests of that type. So, in case you do end up needing to scale your API beyond what route data and route actions can achieve, Solid Start gives you tools to migrate gradually. All that said, Solid Start is definitely beta software. Some of the rough edges I’m hoping get fixed before the 1.0 release: When rendering HTML on the server, Solid Start adds a bunch of weird data-hk attributes to all the elements. Some naming conventions aren’t really clear, and the documentation doesn’t explain them. For example, some functions use $ as a suffix. My guess is that’s for functions that run on the server, but they all have "server" in the name anyway, so it seems redundant. Likewise, I’m not sure what the difference is between functions prefixed with use and create. createServerData$ doesn’t accept a reactive function, and it’s not entirely clear why. Rather than automatically tracking dependencies such as query string parameters, you need to use them to construct a key which determines when to re-fetch the data. This overhead doesn’t exist on "normal" Solid functions like createEffect and createResource. Possibly the most annoying thing is that data is inconsistently serialized when moving between the server and client. In my usage, it seems like objects in the initial page load are returned to the component as actual objects, but once you start taking actions on the page they become strings. It’s a major leak in the client/server abstraction. I’m not sure whether it’s a bug or simply an oversight, but I really hope it gets fixed. Looking Forward After all that, the question is: would I use Solid and/or Solid Start for a "real" project? I think the answer is "not yet". I had a lot of fun building with it, and there are some really promising features. Despite having a lot of experience with React, building a full stack app with Solid took significantly less time and boilerplate. It’s a solid start. But right now, React really is a behemoth when it comes to usage. Solid is definitely my favorite React alternative that I’ve tried, but that doesn’t mean the community agrees. Usage matters because the community is one of the most important metrics to me. Maybe the most important. That’s how a technology gets tested; bugs get found and fixed; plug-ins get created. It’s a bit of a chicken-and-egg situation, because the lack of those things is what prevents the community from forming in the first place. So: for large projects, I’m sticking to React. But for side projects, prototypes and experiments? I’m definitely adding Solid to my toolbox.
Discussion in the ATmosphere