Claude Code for SolidJS Advanced: Fine-Grained Reactivity and SolidStart — Claude Skills 360 Blog
Blog / Frontend / Claude Code for SolidJS Advanced: Fine-Grained Reactivity and SolidStart
Frontend

Claude Code for SolidJS Advanced: Fine-Grained Reactivity and SolidStart

Published: December 28, 2026
Read time: 8 min read
By: Claude Skills 360

SolidJS achieves React-level ergonomics with Svelte-level performance by compiling JSX to direct DOM operations — there’s no virtual DOM, no reconciliation, and no re-renders of components. Signals are reactive variables that notify only their exact dependents when they change. createMemo caches derived values. createStore provides nested reactive state. SolidStart adds file-based routing, server functions ("use server"), and streaming SSR on top. Claude Code generates Solid components, reactive store patterns, SolidStart server functions, and the routing configuration for high-performance Solid applications.

CLAUDE.md for SolidJS Projects

## SolidJS Stack
- Version: solid-js 1.9+, @solidjs/router 0.15+
- Framework: SolidStart 1.0 (file-based SSR + server functions)
- TypeScript: strict mode, solid-ts preset
- Styling: Tailwind CSS or CSS Modules
- State: createSignal (local), createStore (complex objects), Context (cross-component)
- Data fetching: createResource for async, Suspense for loading states
- Testing: vitest + @solidjs/testing-library
- NEVER use React hooks (useState, useEffect) — use Solid equivalents

Core Reactivity Primitives

// components/OrderDashboard.tsx — fine-grained signals
import {
  createSignal, createMemo, createEffect,
  For, Show, Switch, Match,
} from 'solid-js'

interface Order {
  id: string
  status: 'pending' | 'processing' | 'shipped' | 'delivered'
  totalCents: number
  customerName: string
  createdAt: string
}

export function OrderDashboard(props: { orders: Order[] }) {
  const [filter, setFilter] = createSignal<string>('all')
  const [searchQuery, setSearchQuery] = createSignal('')
  const [sortBy, setSortBy] = createSignal<'date' | 'amount'>('date')

  // createMemo: only recomputes when filter, searchQuery, or sortBy changes
  // (not on every unrelated signal update)
  const filteredOrders = createMemo(() => {
    let result = props.orders

    if (filter() !== 'all') {
      result = result.filter(o => o.status === filter())
    }

    const query = searchQuery().toLowerCase()
    if (query) {
      result = result.filter(o =>
        o.customerName.toLowerCase().includes(query) ||
        o.id.includes(query)
      )
    }

    return [...result].sort((a, b) => {
      if (sortBy() === 'date') {
        return b.createdAt.localeCompare(a.createdAt)
      }
      return b.totalCents - a.totalCents
    })
  })

  const totalRevenue = createMemo(() =>
    filteredOrders()
      .filter(o => o.status === 'delivered')
      .reduce((sum, o) => sum + o.totalCents, 0)
  )

  // createEffect: runs when dependencies change (for side effects)
  createEffect(() => {
    document.title = `Orders (${filteredOrders().length})`
  })

  return (
    <div class="p-6">
      <div class="flex gap-4 mb-6">
        <input
          type="search"
          placeholder="Search orders..."
          value={searchQuery()}
          onInput={e => setSearchQuery(e.currentTarget.value)}
          class="border rounded px-3 py-2"
        />

        <select
          value={filter()}
          onChange={e => setFilter(e.currentTarget.value)}
          class="border rounded px-3 py-2"
        >
          <option value="all">All</option>
          <option value="pending">Pending</option>
          <option value="shipped">Shipped</option>
          <option value="delivered">Delivered</option>
        </select>

        <button
          onClick={() => setSortBy(s => s === 'date' ? 'amount' : 'date')}
          class="btn btn-secondary"
        >
          Sort by {sortBy() === 'date' ? 'Amount' : 'Date'}
        </button>
      </div>

      <p class="text-gray-500 mb-4">
        {filteredOrders().length} orders · ${(totalRevenue() / 100).toFixed(2)} revenue
      </p>

      {/* For: efficient list rendering — only updates changed items */}
      <For each={filteredOrders()} fallback={<p>No orders found.</p>}>
        {(order) => (
          <div class="border rounded-lg p-4 mb-3">
            <div class="flex justify-between">
              <div>
                <span class="font-mono text-sm">{order.id}</span>
                <p class="text-gray-700">{order.customerName}</p>
              </div>
              <div class="text-right">
                <p class="font-semibold">${(order.totalCents / 100).toFixed(2)}</p>
                {/* Show: conditional rendering without unmounting */}
                <Show
                  when={order.status === 'delivered'}
                  fallback={<span class="text-orange-500">{order.status}</span>}
                >
                  <span class="text-green-600">delivered</span>
                </Show>
              </div>
            </div>
          </div>
        )}
      </For>
    </div>
  )
}

createStore for Complex State

// stores/order-store.ts — reactive store with nested updates
import { createStore, produce, reconcile } from 'solid-js/store'

interface OrderStore {
  orders: Record<string, Order>
  selectedIds: Set<string>
  loading: boolean
  error: string | null
}

const [store, setStore] = createStore<OrderStore>({
  orders: {},
  selectedIds: new Set(),
  loading: false,
  error: null,
})

// Fine-grained updates — only affected subscribers rerun
function setOrderStatus(orderId: string, status: Order['status']) {
  setStore('orders', orderId, 'status', status)
}

function selectOrder(id: string, selected: boolean) {
  setStore('selectedIds', ids => {
    const next = new Set(ids)
    selected ? next.add(id) : next.delete(id)
    return next
  })
}

// Batch update with produce (immer-like)
async function fetchOrders() {
  setStore('loading', true)

  try {
    const data = await api.getOrders()

    setStore(
      produce(s => {
        s.loading = false
        s.error = null
        // Add all fetched orders
        for (const order of data) {
          s.orders[order.id] = order
        }
      })
    )
  } catch (err) {
    setStore({ loading: false, error: (err as Error).message })
  }
}

// Replace store with fresh data (reconcile preserves references)
function replaceOrders(newOrders: Order[]) {
  const orderMap = Object.fromEntries(newOrders.map(o => [o.id, o]))
  setStore('orders', reconcile(orderMap))
}

export { store, setOrderStatus, selectOrder, fetchOrders }

Context for Cross-Component State

// contexts/auth.tsx
import { createContext, useContext, createSignal, ParentProps } from 'solid-js'

interface AuthContextValue {
  user: () => User | null
  login: (email: string, password: string) => Promise<void>
  logout: () => void
  isAuthenticated: () => boolean
}

const AuthContext = createContext<AuthContextValue>()

export function AuthProvider(props: ParentProps) {
  const [user, setUser] = createSignal<User | null>(null)

  async function login(email: string, password: string) {
    const u = await api.login(email, password)
    setUser(u)
  }

  function logout() {
    api.logout()
    setUser(null)
  }

  return (
    <AuthContext.Provider value={{
      user,
      login,
      logout,
      isAuthenticated: () => user() !== null,
    }}>
      {props.children}
    </AuthContext.Provider>
  )
}

export function useAuth() {
  const ctx = useContext(AuthContext)
  if (!ctx) throw new Error('useAuth must be used within AuthProvider')
  return ctx
}

SolidStart Server Functions

// app.tsx (SolidStart entry) and routes
import { createAsync, query, action, redirect } from '@solidjs/router'
import { Suspense } from 'solid-js'

// Server query — runs on server, result serialized to client
const getOrders = query(async (status?: string) => {
  "use server"
  const db = await getDb()
  return db.orders.findMany({
    where: status ? { status } : undefined,
    orderBy: { createdAt: 'desc' },
    take: 20,
  })
}, 'orders')

// Server action — handles mutations
const updateOrderStatus = action(async (formData: FormData) => {
  "use server"
  const orderId = formData.get('orderId') as string
  const status = formData.get('status') as string

  const db = await getDb()
  await db.orders.update({ where: { id: orderId }, data: { status } })

  return redirect(`/dashboard/orders/${orderId}`)
})


// Route component using server function
export default function OrdersPage() {
  const orders = createAsync(() => getOrders())  // Suspense-aware

  return (
    <Suspense fallback={<div>Loading orders...</div>}>
      <div>
        <For each={orders()} fallback={<p>No orders</p>}>
          {order => (
            <div>
              <span>{order.id}</span>
              {/* Action form — works without JavaScript */}
              <form action={updateOrderStatus} method="post">
                <input type="hidden" name="orderId" value={order.id} />
                <select name="status">
                  <option value="PROCESSING">Processing</option>
                  <option value="SHIPPED">Shipped</option>
                </select>
                <button type="submit">Update</button>
              </form>
            </div>
          )}
        </For>
      </div>
    </Suspense>
  )
}

createResource for Async Data

// Async data loading with createResource
import { createResource, createSignal, Suspense, ErrorBoundary } from 'solid-js'

function OrderDetail(props: { orderId: string }) {
  // createResource fetches and tracks async data
  const [order, { refetch }] = createResource(
    () => props.orderId,  // Source signal — refetches when orderId changes
    async (id) => {
      const resp = await fetch(`/api/orders/${id}`)
      if (!resp.ok) throw new Error(`Order ${id} not found`)
      return resp.json()
    }
  )

  return (
    <ErrorBoundary fallback={(err) => <div>Error: {err.message}</div>}>
      <Suspense fallback={<div class="animate-pulse">Loading...</div>}>
        <Show when={order()}>
          {o => (
            <div>
              <h2>Order {o().id}</h2>
              <p>Status: {o().status}</p>
              <p>Total: ${(o().totalCents / 100).toFixed(2)}</p>
              <button onClick={refetch}>Refresh</button>
            </div>
          )}
        </Show>
      </Suspense>
    </ErrorBoundary>
  )
}

Performance: Why Solid is Fast

// Signals are NOT reevaluated on render — they ARE reactive atoms
// This code demonstrates the difference from React:

// React re-renders the ENTIRE component when state changes
// function ReactComponent() {
//   const [count, setCount] = useState(0)
//   return <div><span>{count}</span></div>  // Full re-render on each click
// }

// Solid: ONLY the <span> DOM node updates — no component re-execution
function SolidComponent() {
  const [count, setCount] = createSignal(0)

  // This function runs ONCE — not on every update
  console.log("Component function ran once")

  return (
    <div>
      {/* Only updates the text node — zero extra work */}
      <span>{count()}</span>
      <button onClick={() => setCount(c => c + 1)}>+</button>
    </div>
  )
}

For the React alternative with a larger ecosystem and similar component patterns, see the React 19 guide for server components and hooks. For the Svelte 5 runes-based reactivity that achieves similar fine-grained updates through a compiler, the SvelteKit advanced guide covers Svelte’s reactive model. The Claude Skills 360 bundle includes SolidJS skill sets covering signals, stores, and SolidStart server functions. Start with the free tier to try SolidJS component generation.

Keep Reading

Frontend

Claude Code for Chart.js Advanced: Custom Plugins and Mixed Charts

Advanced Chart.js patterns with Claude Code — chart.register() for tree-shaking, mixed chart types combining bar and line, custom plugin API with beforeDraw and afterDatasetsDraw hooks, ScriptableContext for computed colors, ChartDataLabels plugin for value labels, chartjs-plugin-zoom for pan and zoom, custom gradient fills via ctx.createLinearGradient, ChartJS annotation plugin for threshold lines, streaming data with chartjs-plugin-streaming, and react-chartjs-2 with useRef and chart instance.

6 min read Jun 27, 2027
Frontend

Claude Code for Nivo: Rich SVG and Canvas Charts

Build rich data visualizations with Nivo and Claude Code — ResponsiveLine and ResponsiveBar for adaptive charts, ResponsiveHeatMap for matrix data, ResponsiveTreeMap for hierarchal data, ResponsiveSunburst for nested proportions, ResponsiveChord for relationship diagrams, ResponsiveCalendar for activity heat maps, ResponsiveNetwork for force graphs, NivoTheme for consistent styling, tooltip customization with sliceTooltip, and motion config for spring animations.

6 min read Jun 26, 2027
Frontend

Claude Code for Victory Charts: React Native and Web Charts

Build cross-platform charts with Victory and Claude Code — VictoryChart, VictoryLine, VictoryBar, and VictoryScatter for web and React Native, VictoryPie for donut charts, VictoryArea for stacked areas, VictoryAxis for custom axes, VictoryTooltip and VictoryVoronoiContainer for hover tooltips, VictoryBrushContainer for range selection, VictoryZoomContainer for pan and zoom, VictoryLegend for series labels, custom theme with VictoryTheme, and VictoryStack for grouped bars.

6 min read Jun 25, 2027

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free