Getting Started
Sublime is one TypeScript codebase that spans mobile, web, and desktop — real platform-native UI (MUI on web, React Native Paper on mobile), a Laravel/Eloquent-inspired Model layer on the frontend, and navigation plus a native bridge that are generated and type-checked at compile time.
This path takes you from your first Model to a packaged desktop app in three stages. Each stage tells you what you'll be able to build by the end and what to learn next. Work through them in order — every stage builds on the one before it.
Foundations (Beginner)
Goal: stand up a Sublime workspace, model your data, theme the app, and render reactive screens.
The mental model
A Sublime app is one workspace with a clear split:
- Shared core —
components/,models/,theme/, and native service contracts. Written once, used everywhere. - Per-platform screens —
screens/web/andscreens/mobile/, plus each platform's navigation. Desktop reuses the web UI; there are no separate desktop screens.
Start with the Framework Overview and Your First App to see the layout end to end.
Skills you'll learn
-
Define a Model. Extend
Model, declare fields withdeclare, pointresourceat the REST collection, then callregisterModel(User). Registration wires a fetch Gateway (CRUD overresource), an auto-registering Redux slice, and a discovery registry.import { Model, registerModel } from '@sublime-ui/framework';export class User extends Model {protected static resource = '/users';declare id: number;declare name: string;declare role: 'admin' | 'member';}registerModel(User); -
Read data two ways. Use the async commands
User.all()andUser.find(id)when you need a promise (they throwApiErroron failure), and the reactive hooksUser.rxAll()/User.rxFind(id)inside React components — these are cache-first and fetch-and-cache when data is missing. -
Theme with tokens. Author a serializable
SublimeTokensobject, wrap the app in<SublimeProvider tokens={tokens}>, and read tokens anywhere withuseTokens(). Tokens generate a Paper theme (mobile) and a MUI theme (web) from one source. -
Use components. Drop in library components (
AppBar,GlassAppBar, and ~21 others). The bundler resolves each one per platform by filename, so you import once and get the native widget on each target. -
Notify the user. Call
useNotify()— the same call raises a snackbar on mobile and a toast on web.
Concepts & APIs: Model, registerModel, declare fields, resource, all() / find() / rxAll() / rxFind(), ApiError, SublimeTokens, <SublimeProvider>, useTokens(), useNotify().
Docs: Framework Overview · Library Overview · Your First App
By the end you can scaffold a workspace, define models, read live data into themed, native-looking screens, and surface notifications.
Next: learn how to navigate between screens and write the data back.
Building apps (Intermediate)
Goal: turn a set of screens into a navigable app per platform, lay out content with primitives, and write/read data including custom endpoints.
Skills you'll learn
-
Author platform screens. Web screens live in
screens/web/, mobile screens inscreens/mobile/. Shared models, components, theme, and native contracts stay in the core — only the screens and each platform's storybook differ. -
Compose a storybook. Describe navigation with
book,page, andlinkfrom@sublime-ui/ui/navigation, authored separately per platform (storybook.web.ts,storybook.native.ts). Mobile formats are'drawer' | 'stack' | 'bottomNav'(the latter limited to ≤ 5 pages); web formats are'sidebar' | 'stack' | 'tabs'. A mobile book can't use a web format, and vice versa — it's a type error.import { book, page, link } from '@sublime-ui/ui/navigation';export default book({format: 'bottomNav',pages: {home: page(Home, { title: 'Home', icon: 'home' }),product: page<{ id: number }>(ProductDetail, { title: 'Product' }),settings: link(settingsBook, { title: 'Settings', icon: 'cog' }),},}); -
Compile and navigate. Run
sublime build:navto generate React Navigation (mobile), react-router (web), a typedroutes.d.ts, and a platform-resolved<Navigation/>. At runtime callconst nav = useNav();thennav.turnTo('product', { id: 1 })— typed against the generated route map, so an unknown page name or wrong/missing params is a compile error. Alsonav.turnBack(),nav.current(),nav.params<T>(). -
Lay out screens. Build with the platform-resolved primitives
Screen,Stack,Row, andSpacer. -
Write and delete data. Use the async instance commands
user.save()anduser.delete()(both throwApiErroron failure). The store keeps plain JSON viahydrate/toPlain. -
Call custom endpoints. Reach beyond plain CRUD with
Model.call(...)for endpoints that aren't standard resource routes.
Concepts & APIs: book / page / link, mobile vs web formats, page<Params> and typed turnTo, link for nesting books, sublime build:nav, useNav() (turnTo / turnBack / current / params), Screen / Stack / Row / Spacer, save() / delete(), Model.call(...).
Docs: Navigation · Models · Components & theming
By the end you can ship a real multi-screen app on web and mobile from one shared core — navigable, laid out, and reading and writing data through your models.
Next: reach into native capabilities and package the app for the desktop.
Going native & shipping (Advanced)
Goal: call native device capabilities through the secure bridge, package a desktop build, produce offline Android builds, and understand where the platform is headed.
Skills you'll learn
-
Define a native service. In
src/native/, declare a service withdefineNative— main-process code that may import node deps. Export its type so the renderer can import it as a type only.import { defineNative } from '@sublime-ui/desktop';export const printer = defineNative('printer', {async print(receipt: Receipt): Promise<void> { /* node code */ },async listDevices(): Promise<Device[]> { /* ... */ },});export type Printer = typeof printer; -
Register and consume it. Register services in the Electron main process with
registerNative([fs, dialog, shell, clipboard, notifications, printer]), then call from any renderer screen withuseNative<Printer>('printer')— which isnullon plain web, so you guard withprinter?.print(receipt). -
Understand the security model. Everything flows over one generic IPC channel (
native:invoke). The renderer imports only theimport typeof a service, so node deps never enter the web bundle. The shell runs withcontextIsolation: trueandnodeIntegration: false, and the main router rejects any(module, method)pair that wasn't registered; failures surface as a typedNativeError. Built-in services includefs,dialog,shell,clipboard, andnotifications. -
Package for desktop with Electron Forge. Run
sublime dev:desktopfor Forge start with HMR, andsublime build:desktopto make Windows/macOS/Linux packages. Desktop renders the web UI — no separate desktop screens. -
Build offline for Android. Use
sublime buildfor an offline Android APK andsublime runto install and launch on a connected device.
Concepts & APIs: defineNative, registerNative, useNative<T>, the native:invoke channel, contextIsolation / nodeIntegration hardening, NativeError, built-in services (fs / dialog / shell / clipboard / notifications), sublime dev:desktop / build:desktop, sublime build / run.
Docs: Native calls · Desktop · Packaging
Data, your way
Models talk to a swappable Gateway: HttpGateway for a REST API (responses shaped ApiResponse<T> = { success, message, data, errors }) or DbGateway for a local database — IndexedDB on web, SQLite on desktop and mobile — with the same Model API either way, no screen changes. See Data & persistence.
By the end you can call native hardware safely from a shared codebase, ship a packaged desktop app, produce offline Android builds, and reason about where Sublime is going next.