Skip to main content

Your First App

This walkthrough builds a tiny but real Sublime app end to end: design tokens, a model, a screen on each platform, navigation authored per platform and compiled, and live data reads. Every command and API here is real.

1. Initialize your theme

Sublime's design system is tokens-first: a single serializable SublimeTokens object drives both the web (MUI) and mobile (Paper) themes.

npx @sublime-ui/devkit theme:init

This scaffolds your theme/ directory with tokens.json and a tokens.ts that exports the tokens object.

2. Generate a model

Models are the heart of a Sublime app — a Laravel/Eloquent-style data layer. Generate one with make:model:

npx @sublime-ui/devkit make:model User --fields "name:string"

That produces a User model under src/models/:

import { Model, registerModel } from '@sublime-ui/framework';

export class User extends Model {
protected static resource = '/users';
declare id: number;
declare name: string;
}
registerModel(User);

registerModel(User) wires up a fetch-based Gateway (CRUD over resource), an auto-registering Redux slice, and a discovery registry — so the model is ready to read and write with no extra boilerplate.

3. Wrap your app in the provider

generateThemes turns your tokens into the per-platform theme objects, and <SublimeProvider> makes them (and useTokens()) available everywhere. Wrap your app's root in it:

import { SublimeProvider } from '@sublime-ui/library';
import { tokens } from './theme/tokens';

export function App() {
return (
<SublimeProvider tokens={tokens}>
{/* navigation + screens go here */}
</SublimeProvider>
);
}

4. Create a screen for each platform

Screens are platform-specific — one per target. Create a web screen and a mobile screen that both list users.

// src/screens/web/Home.tsx
import { Screen, Stack } from '@sublime-ui/ui';
import { User } from '../../models/User';

export function Home() {
const users = User.rxAll();
return (
<Screen>
<Stack>
{users.map((u) => (
<div key={u.id}>{u.name}</div>
))}
</Stack>
</Screen>
);
}
// src/screens/mobile/Home.native.tsx
import { Screen, Stack } from '@sublime-ui/ui';
import { Text } from 'react-native-paper';
import { User } from '../../models/User';

export function Home() {
const users = User.rxAll();
return (
<Screen>
<Stack>
{users.map((u) => (
<Text key={u.id}>{u.name}</Text>
))}
</Stack>
</Screen>
);
}

User.rxAll() is a reactive, cache-first hook: it returns whatever is in the store immediately and fetches over the Gateway if the data is missing.

5. Author a storybook per platform

Navigation in Sublime is a storybook — a book of pages, authored separately for each platform. A mobile book uses mobile formats; a web book uses web formats. Mixing them is a type error.

// src/navigation/storybook.native.ts (mobile)
import { book, page } from '@sublime-ui/ui/navigation';
import { Home } from '../screens/mobile/Home.native';

export default book({
format: 'bottomNav', // mobile: 'drawer' | 'stack' | 'bottomNav' (<= 5 pages)
pages: {
home: page(Home, { title: 'Home', icon: 'home' }),
},
});
// src/navigation/storybook.web.ts (web)
import { book, page } from '@sublime-ui/ui/navigation';
import { Home } from '../screens/web/Home';

export default book({
format: 'sidebar', // web: 'sidebar' | 'stack' | 'tabs'
pages: {
home: page(Home, { title: 'Home' }),
},
});

You can type a page's params with page<Params>(Screen, opts?), and nest another book with link(book, opts?):

import { book, page, link } from '@sublime-ui/ui/navigation';
import { ProductDetail } from '../screens/mobile/ProductDetail.native';
import { settingsBook } from './settings.native';

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' }),
},
});

6. Compile your navigation

Storybooks are compiled ahead of time into idiomatic, fully typed navigation:

npx @sublime-ui/devkit build:nav

This generates navigation.native.tsx (React Navigation), navigation.tsx (react-router), a typed route map (routes.d.ts), and an index barrel that exports the platform-resolved <Navigation/>.

7. Mount the generated navigation

Drop the generated <Navigation/> inside your provider:

import { SublimeProvider } from '@sublime-ui/library';
import { Navigation } from './navigation';
import { tokens } from './theme/tokens';

export function App() {
return (
<SublimeProvider tokens={tokens}>
<Navigation />
</SublimeProvider>
);
}

At runtime, useNav() gives you type-checked navigation: nav.turnTo('product', { id: 1 }) (params are required exactly when the page declares them), nav.turnBack(), nav.current(), and nav.params<T>(). An unknown page name or wrong params is a compile-time error.

8. Read your data

Your screens already call User.rxAll(), which is all you need to render a live, cache-first list of users. The same model also offers async commands when you need them — User.all(), User.find(id), user.save(), user.delete(), and User.call(...) for custom endpoints (these throw ApiError on failure).

That's a complete Sublime app: shared tokens and model, a screen per platform, compiled navigation, and reactive data — ready to run on web, mobile, and desktop.