Skip to content
JSX / compiledstatic templateprecise DOM ops

Build in
sunlight.

Sol compiles familiar JSX into static HTML templates and fine-grained DOM operations. Setup runs once. Updates land exactly where data changed.

bun add sol
DOMonly what changes
Block / 01Writable statelet count = 0
Block / 02Computedcount * 2
Block / 03Binding$bind={state}

01 / The mechanism

Familiar in.
Focused out.

The compiler traces dependencies before code reaches the browser. The runtime gets a static template and only the operations needed to keep it current.

  1. 01

    Write a component

    Plain declarations, derived values, JSX.

    $component(fn)
  2. 02

    Compile the graph

    Static template plus dependency-aware DOM work.

    ↘ ↗
  3. 03

    Patch with precision

    No component rerun; dependent DOM updates directly.

    mounted

02 / Working assemblies

Code with gravity.

Switch between code, preview, or both. Every preview is compiled by Sol and keeps its state while the panels move.

01 / Reactivity

Normal reads. Direct updates.

Counter.sol.tsx
import { $component, $query, $rpcQuery } from "sol";import * as v from "valibot";export const websiteMessage = $rpcQuery(  "website-message",  { schema: v.tuple([]) },  async () => ({ message: "Validated on the Sol server." }),);const Counter = $component(function Counter() {  let count = 0;  const doubled = count * 2;  const serverMessage = $query({    queryKey: ["website", "message"],    query: websiteMessage,    enabled: false,  });  return (    <>      <button onClick={() => count++}>{count} / {doubled}</button>      <button onClick={() => void serverMessage.refetch({ suspense: false })}>        {serverMessage.isFetching ? "Calling server…" : "Call named RPC"}      </button>      {serverMessage.data && <p>{serverMessage.data.message}</p>}    </>  );});

Reactive output

0

doubled / 0

02 / Deep state

Mutate the data you own.

SolarList.tsx
import { $component } from "sol";const SolarList = $component(function SolarList() {  let items = [{ id: 1, label: "Static template", ready: true }];  return <ul>{items.map(item => (    <li key={item.id}>      <button onClick={() => item.ready = !item.ready}>        {item.ready ? "Ready" : "Draft"} — {item.label}      </button>    </li>  ))}</ul>;});
03 / Validation

Parsed output crosses the boundary.

Signup.tsx
import { $component, $form } from "sol";import * as v from "valibot";const Email = v.object({  email: v.pipe(v.string(), v.email("Enter a valid email.")),});const Signup = $component(function Signup() {  const form = $form({    schema: v.parser(Email),    defaultValues: { email: "" },  }, values => console.log(values));  return <form $form={form}>    <input name="email" $bind={form.values.email} />    <button>Join</button>  </form>;});

Only parsed output reaches submit.

Sol

Experimental JSX framework / Sunblock system

Static blocks + precise motion