SolidMVC

A frontend framework that takes Angular's architecture and renders it with React's ergonomics: modules, dependency injection, and decorators around class-based JSX views on Solid, with signal reactivity and worker-rendered islands built in.

The idea

Angular got application shape right — modules that own providers, injectors that scope services, decorators that declare intent, an MVC split between views and controllers. React got rendering right — components are values, JSX composes freely, and imports resolve identity instead of a template registry. SolidMVC keeps both: you write JSX with ordinary imports, and organize it with modules and DI.

counter.view.tsx
import { Component, Controller, View, inject, signal } from '@solid-mvc/core';

@Controller()
class CounterController {
  count = signal(0);
  inc = () => this.count.value++;
}

// A view is a class. Injection happens once at mount — no hooks in render().
@Component({ controllers: [CounterController] })
export class CounterView extends View {
  private c = inject(CounterController);
  render() {
    return <button onClick={this.c.inc}>count: {this.c.count.value}</button>;
  }
}

Three packages

PackageWhat it is
@solid-mvc/coreThe framework: injector, decorators (@Module, @Component, @Injectable, @Controller), View classes, signals, context, router. One dependency: solid-js.
@solid-mvc/atollFacade over @atolljs/islands + @atolljs/core: the op protocol, the proxy DOM a worker renders into, the worker runtime, the island contracts, and the main-thread driver that replays ops onto real DOM.
@solid-mvc/atoll-islandThe binding package: <Island/> shell components on the main thread, defineViewPolyWorker/viewIslandApp on the worker side. The same View classes render in both places.

What it borrows from each side

From AngularFrom Solid
Modules that own providers and declare componentsComponents are values you import, not registered selectors
Hierarchical injectors, provider scopes, inject()JSX everywhere — no template language, no compile step
Decorators for metadata (@Module, @Component)Freedom of composition — undeclared views mount anywhere
MVC split: controllers own interaction stateSignals for fine-grained reactivity
Standalone components (@Component imports)Class construction replaces per-render hook calls

The whole Solid ecosystem, unchanged

render() returns ordinary Solid JSX — anything that works in a Solid app works here, no "framework bindings" version of the library:

  • Any Solid component — icon sets, UI kits, form controls — drops straight into a view's JSX. Control flow stays idiomatic: <For>, <Show>, context, portals.
  • Fine-grained by construction — the framework's signals wrap Solid's own reactive graph, so reads inside JSX update exactly the DOM that read them — no re-render, no diff.
  • Anything else is two moves: provide the client object through DI, bridge its subscribe into a signal. DOM-bound libraries (charts, editors, maps) get a three-line ref wrapper — see Integrating libraries.

The enterprise demo proves it in practice: TanStack Query Core, ECharts, zod, date-fns, lucide-solid icons, and a Tailwind/shadcn component layer all run through those moves. Inside worker islands the same View classes render too — even ECharts runs there, emitting SVG through the proxy DOM.

And one thing neither has

Worker-rendered islands. A <Island app="ticker" worker={...} /> mounts a real SolidMVC view tree inside a Web Worker — the same View classes, signals, and DI — while the shell sees an ordinary element that ticks, emits events, and never blocks the main thread. See Islands.

Where to go next