Getting Started
1. Install
pnpm add @dev-bench/gitapp-ui react react-native react-native-svgFor react-native-web apps, also ensure react-native-web and your bundler's
RN ↔ web aliases are in place — this package only renders RN primitives, so
the web alias is what makes it work in a browser.
2. Wrap the app in ThemeProvider
ThemeProvider injects design tokens (colors, fonts, radii, spacing). Every
component in this package reads from it via useTheme() — there are no
hardcoded colors.
import { ThemeProvider, defaultLightTheme } from "@dev-bench/gitapp-ui";
export function Root({ children }) {
return <ThemeProvider theme={defaultLightTheme}>{children}</ThemeProvider>;
}To override a token (brand color, font), pass a partial theme through
mergeTheme:
import { ThemeProvider, defaultLightTheme, mergeTheme } from "@dev-bench/gitapp-ui";
const brandTheme = mergeTheme(defaultLightTheme, {
colors: { brand: "#7c3aed", brandBg: "#f5f3ff" },
});
<ThemeProvider theme={brandTheme}>...</ThemeProvider>3. Drive a selector with a Resource<T> view-model
Every selector takes a single model prop. The model exposes the current
value plus a Resource<T> that tracks the lifecycle of the most recent action.
Use the constructors on Resource — they encode the four canonical states.
import { Resource } from "@dev-bench/gitapp-ui";
Resource.idle(); // nothing happening
Resource.loading(); // pending request
Resource.ready(undefined); // success
Resource.error({ message: "503 from github.com" });A minimal MobX view-model for ProviderSelector:
import { makeAutoObservable, runInAction } from "mobx";
import { Resource, type IProviderSelectorModel } from "@dev-bench/gitapp-ui";
export class ProviderModel implements IProviderSelectorModel {
current = { provider: "github" as const, host: "github.com" };
state = Resource.idle<void>();
isOpen = true;
constructor(private readonly api: { saveProvider(p, h): Promise<void> }) {
makeAutoObservable(this);
}
onSelectProvider(p) { this.current = { ...this.current, provider: p }; }
onCustomHostChange(host) { this.current = { ...this.current, host }; }
onDismiss() { this.isOpen = false; }
async onConfirm() {
this.state = Resource.loading();
try {
await this.api.saveProvider(this.current.provider, this.current.host);
runInAction(() => { this.state = Resource.ready(undefined); });
} catch (e) {
runInAction(() => {
this.state = Resource.error({ message: e instanceof Error ? e.message : String(e) });
});
}
}
}4. Render the selector
Wrap the component in MobX's observer so the selector reacts to model
changes:
import { observer } from "mobx-react-lite";
import { ProviderSelector } from "@dev-bench/gitapp-ui";
export const ProviderScreen = observer(({ model }: { model: ProviderModel }) => (
<ProviderSelector model={model} />
));You don't have to use MobX — any reactive store (Zustand, Jotai, plain React state) that produces a fresh model object on update will work, as long as it hands the component a model implementing the interface.
5. Add a repo picker
RepoPickerSheet follows the same shape — feed it a model with repos,
selected, and the lifecycle state:
import { RepoPickerSheet, type IRepoPickerModel } from "@dev-bench/gitapp-ui";
export const RepoPickerScreen = observer(({ model }: { model: IRepoPickerModel }) => (
<RepoPickerSheet model={model} />
));See the API reference for ConnectionPanel, primitives, and the
exported types.