Components
GitApp UI

Getting Started

1. Install

pnpm add @dev-bench/gitapp-ui react react-native react-native-svg

For 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.

On this page