Skip to content

v0.4.0 - Beta 2

Compare
Choose a tag to compare
@ryansolid ryansolid released this 20 Dec 05:05
· 585 commits to main since this release

It's been a long year and we couldn't be more thrilled to see all the amazing projects built on SolidStart. Things like https://post.news being built on Start among others. We've had incredible support from companies and contributors, including the Chrome team's help in our research into performance increasing technology. And we were able show the world new technologies like Server Functions that were able to be adopted by other frameworks like Qwik and Next.js.

However, we've learned a lot in this year, both in terms of our technology approach and the efforts in maintaining a metaframework. What we had done worked, but it didn't work well and it became apparent early in the year we needed to do something about it. And that itself was a journey into exploring different technologies. In the end we settled on Nitro to provide our server layer for Start. Together with more effectively breaking apart responsibilities into existing ecosystem libraries we were able to reduce the maintenance of SolidStart considerably dropping the lines of code from over 10k to about 1400 across 32 files instead of the original 150.

My talk at Vite conference this year highlights the thinking that went into this.

The culmination of this work is this new beta, we are calling Beta 2. It is a reset in a sense. A new beta that better delivers on what we set out to do in the first place. It is a smarter beta, one that better isolates change and will allow us to evolve the framework with the technology. Technology we have today and technology we'll discover tomorrow. That means that the path to version 1.0 will be much shorter because we don't have to worry about being way ahead of the curve. We'll be able to release features as they are ready without breaking things.

I want to give a big thanks to @nksaraf, @lxsmnsyc, and everyone involved in the RFC process and provided feedback in the discord of the past few months. Without you things wouldn't be where they are today. Alright lets talk about the features.

1. Rebase to Nitro's adapters and Vinxi

As mentioned we are now based on Nitro, the same engine that powers Nuxt and Analog. This means we can leverage the same platforms and work together to improve features across the ecosystem. It takes a lot of effort to build for each platform so having this shared base is incredibly important. You can access your Nitro settings under the start.server settings in your vite config.

import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
  start: {
    server: {
      /* NITRO SETTINGS */
    }
  }
});

This means full access to pre-rendering in any project including automated link crawling, dozens of built-in presets for deployment, the full power of H3 a universal HTTP server, and cozy integration with the rest of the unjs ecosystem.

We are using Vinxi as well as a bundler layer. Vinxi is a "Bun App" inspired meta-bundler that sits on top of Vite and wires together assets, server functions, file system routing as a generic framework agnostic layer. The combination of these 2 libraries makes up the majority of the functionality of SolidStart now.

2. Router/Metadata Agnostic

A big part of the goal here is to let any existing Solid library work with Solid Start, including routers. In so we, have removed @solidjs/router and @solidjs/meta as being a core dependency and instead made the FileSystem routing consumable by any router. We've adapted the application entry point in such a way that you can insert your Providers as you wish.

// app.tsx
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start"; // only thing imported from SolidStart
import { Suspense } from "solid-js";
import "./app.css";
import Nav from "./components/nav";

export default function App() {
  return (
    <Router
      root={props => (
        <>
          <Nav />
          <Suspense fallback={<div class="news-list-nav">Loading...</div>}>
            {props.children}
          </Suspense>
        </>
      )}
    >
      <FileRoutes />
    </Router>
  );
}

And the "page" entries support a route config that you can match to any router you wish.

// whatever route props you want send to your router
export const route = {
  load({ params }) {
    void getUser(params.id);
  }
} satisfies RouteDefinition;

export default function PageComponent(props: RouteSectionProps) { }

It feels a lot like create-react-app or create-vite-app except it works with SSR and deployment out of the box.

3. "use server" Server Functions

async function callMeAnywhere(id: number) {
  "use server";
  const res = await db.findSomething({ where: { id } });
  return res.data;
}

server$ was a pretty powerful convention for server functions. We were able to type the function and even pass in config options. However, with Solid Beta 2 we pick up the React convention and move to "use server";. This has other advantages of allowing for module level scope, makes it more visually clear that a server function is different, and it is consistent with React ecosystem which will make it easier for us to leverage work from other bundlers. In a way this is similar to our choice to use JSX with Solid. There are some key differences mind you.

  • Our server functions are just functions, no more or less. Server actions are taking server functions and using them with actions from Solid Router. actions are a navigation consideration that are independent of whether the navigation happens in the browser or the server.

  • Our server functions are RPC in the browser but are just plain function calls on the server. They can be called on the server or on the client as needed.

  • Our server functions exist independent of the concept of Server Components. They can be declared anywhere. They do not do closure extraction so you can only close over variables at a module scope. They will not automatically grab server code and serialize it into the client.

The benefit of this approach is these server functions will work with any existing promise library whether built in primitives like createResource or 3rd party like createQuery from Tanstack Query.

Our server functions also got a serious shot in the arm now using Solid's serializer, Seroval, to handle the encoding letting us stream back a variety of data formats, sending promises or even async iterators over the wire.

4. Client Rendered SPAs are still very real

We are serious about supporting existing applications. Sometimes the move to server rendering isn't easy. Luckily you can just toggle ssr: false in your config. But its more than that. Just because you have a client rendered SPA doesn't mean you shouldn't be able to benefit from easy APIs with Server Functions. Client Rendered only apps can still use server functions.

When you make the decision to do server rendering, beyond just flicking the switch we provide a clientOnly wrapper that allows you to define code that can't run on the server to be lazily loaded in the client and run after hydration. Things like jQuery plugins or big browser only data-grids now can now work seamlessly with SSR.

import { clientOnly } from "@solidjs/start"

const BigDataGrid = clientOnly(() => import("../BigDataGrid"));

function IsomorphicComp() {
  return <BigDataGrid />
}

5. Everything Else

Honestly, there are a bunch of other new features that come with this release, but that doesn't belong here but in their respective libraries. But that's the point. @solidjs/router now brings createAsync, cache and action data APIs, along with link prefetching. We feel this is a drastic improvement over the named createRouteData and createServerData$ APIs with much better TypeScript support. @solidjs/meta now offers zero configuration SSR head injection. But neither is owned by SolidStart. You can continue use the old version of the @solidjs/router with its data function paradigm with the new SolidStart or upgrade to the latest and it doesn't matter.

What you have here are the primitives you need from a metaframework, FileSystem Routing, Advanced Bundling for things like Server Functions/Islands, and a universal Server Runtime and deployment system.

Note: Some experimental features like Islands Routing are not in this Beta as they haven't made their way to their new home yet. And Islands is still experimental. But now that we have the right foundations these should be additive as they become available.

The Road to 1.0

I apologize it took so long to get here. But we're very happy with this separation of responsibility. Separating the opinionated stuff from the more stable and build a Solid foundation for building applications with Solid in the future. This approach took a while to work out but it should also significantly expedite our move to 1.0.

There are pieces to iron out, in terms of TypeScript and testing, bugs to stomp out on deploy and bundling, and documentation to be written but SolidStart has gone from an ambitious project to a project that can be used to build ambitious projects. We thought we were in a good place a year ago, but only now have we really accomplished what we set out to do, create a metaframework for those of us who don't like metaframeworks.

Thanks for coming on this journey with us.