Guides

Building for mobile

Three routes get a Solid app onto iOS and Android:

  • WebView shell: a native app embeds a browser engine (a WebView) that runs your web build. Use Capacitor or Tauri.
  • DOM shim over native views: Solid renders to real native views through a DOM-like layer. Use NativeScript.
  • Custom renderer: you implement a renderer with solid-js/universal and drive native views directly.

Solid's reactivity is the same in every route. What changes is what your components render to, which decides how much of your markup and CSS carries over.


Comparing the routes

Renders toMarkup and stylingNative APIsPick it when
CapacitorWebViewHTML and CSS, unchangedJavaScript plugins backed by Swift and Java or KotlinYou want to ship an existing web app with few changes
TauriSystem WebViewHTML and CSS, unchangedRust commands, plus plugins in Rust, Swift, and KotlinYou want a small binary and a Rust backend
NativeScriptNative viewsNativeScript elements instead of HTMLDirect access to platform APIs from JavaScriptYou want native views without writing a renderer
Custom rendererNative views, or any non-DOM hostThe host's own elementsWhatever the host providesNo existing integration covers your platform

WebView shells

A WebView is a browser engine embedded in a native app. The shell loads your Solid SPA into it, so components, routing, and CSS run as on the web, and plugins bridge to native features like the camera or push notifications.

Capacitor

Capacitor wraps a web build in a native iOS/Android project and exposes native APIs to your Solid app through JavaScript plugins. The ionic-team/capacitor-solidjs-templates repository provides official starter templates for Solid with Capacitor already configured.

Tauri

Tauri uses the platform's system WebView instead of bundling one, for a smaller binary. Its Solid frontend guide covers the setup, and Tauri 2 targets iOS and Android.


NativeScript

NativeScript renders native views and exposes platform APIs to JavaScript. @nativescript-community/solid-js shims a DOM-like API over those views, so Solid's regular DOM renderer drives them without a WebView.

Your reactive code carries over, but your markup does not: you write NativeScript elements such as <stacklayout> and <label>, not HTML.


Custom renderers with solid-js/universal

Solid's default renderer, solid-js/web, assumes a DOM. A host without one, such as native views, needs its own way to create, update, and remove nodes, and solid-js/universal exposes createRenderer for that. It takes an object describing how to create, mutate, and traverse nodes on your host, and returns Solid's primitives (render, effect, memo, insert, and more) wired to it:

import { createRenderer } from "solid-js/universal";
const {
render,
effect,
memo,
createComponent,
createElement,
createTextNode,
insertNode,
insert,
spread,
setProp,
mergeProps,
use,
} = createRenderer({
createElement(tag) {
/* create a node for your host */
},
createTextNode(value) {
/* create a text node for your host */
},
replaceText(textNode, value) {
/* update a text node's value */
},
setProperty(node, name, value) {
/* apply a prop/attribute to a node */
},
insertNode(parent, node, anchor) {
/* attach node to parent, before anchor if given */
},
removeNode(parent, node) {
/* detach node from parent */
},
isTextNode(node) {
/* return whether node is a text node */
},
getParentNode(node) {},
getFirstChild(node) {},
getNextSibling(node) {},
});

That lets Solid's fine-grained reactivity drive any tree-shaped host, not just the DOM. createRenderer only covers creating and updating nodes. Native views, a layout engine, and gesture handling are not included, so building on it directly is a project of its own.

For the full createRenderer signature, see its source on GitHub.

Last updated: 10/10/26, 2:23 AMEdit this pageReport an issue with this page