A plugin-based React Native library of widgets, components, and services for building configuration-driven health research apps.
The repo root is the library. A runnable host template lives in starter-kit/ and consumes the library exactly the way any other study app would — clone, rename, drop in a config, ship.
radar-base-app/ <- the library (publishable as @radarbase/app-kit)
├── src/ <- library source (TypeScript)
│ ├── core/ <- services: ApiService, ConfigService, AuthService, EventBus, ...
│ ├── library/
│ │ ├── app-shell/ <- AppShell, ThemeProvider, PluginManager
│ │ ├── config/ <- configLoader + strategies + validation
│ │ └── contracts/ <- type-only public contracts
│ ├── widgets/ <- built-in widgets + WidgetFactory + WidgetRegistry
│ └── index.ts <- public API surface
├── lib/ <- tsc build output (what consumers import)
├── starter-kit/ <- clone-and-rename host template (consumes the library)
├── docs/
├── scripts/dev.sh
├── package.json <- main: lib/index.js, types: lib/index.d.ts
└── tsconfig.json <- rootDir: src, outDir: lib
- Widget system:
WidgetFactory+WidgetRegistryfor plug-and-play UI from JSON/YAML config. - Built-in widgets: Questionnaire, TaskList, Dashboard, DeviceStatus, Calendar.
- App shell:
AppShell,ThemeProvider,PluginManagerfor composing a configurable host app. - Core services:
CoreServicesProvider+useCoreServices()forApiService,ConfigService,AuthService,EventBus,DataService, etc. - Configuration-driven:
configLoaderwithLocalFileStrategy,RemoteUrlStrategy,ServerStrategy; runtime validation viavalidateAppConfig+defaultConfig. - TypeScript-first: full type definitions for all config and props.
npm install @radarbase/app-kitPeer dependencies (the host provides these — React Native projects already have most of them):
react >=16.8,react-native >=0.60- Optional peers used by certain services / widgets:
@react-native-firebase/app,/analytics,/messaging,/remote-config@react-native-async-storage/async-storagereact-native-keychain
Everything is imported from the package root. You should never reach into lib/... paths.
import {
AppShell,
ThemeProvider,
PluginManager,
WidgetFactory,
WidgetRegistry,
CoreServicesProvider,
useCoreServices,
configLoader,
validateAppConfig,
defaultConfig,
LocalFileStrategy,
ServerStrategy,
} from '@radarbase/app-kit';
import type { AppConfig, WidgetConfig } from '@radarbase/app-kit';import React from 'react';
import {
AppShell,
ThemeProvider,
CoreServicesProvider,
validateAppConfig,
defaultConfig,
} from '@radarbase/app-kit';
import appConfig from './config/myStudy.json';
export default function App() {
const { valid, config } = validateAppConfig(appConfig);
const resolved = valid ? config : defaultConfig;
return (
<CoreServicesProvider>
<ThemeProvider theme={resolved.theme}>
<AppShell config={resolved} />
</ThemeProvider>
</CoreServicesProvider>
);
}import { useCoreServices } from '@radarbase/app-kit';
function MyWidget() {
const { api, config, auth, eventBus } = useCoreServices();
// call api.get(...), config.get(...), auth.signIn(...), eventBus.emit(...)
}import { WidgetRegistry } from '@radarbase/app-kit';
import MyCustomWidget from './MyCustomWidget';
WidgetRegistry.getInstance().register('my-custom', MyCustomWidget);Once registered, you can reference it from configuration by type: 'my-custom' and WidgetFactory will render it.
The library is fully configuration-driven. The AppConfig shape covers theme, header, and a nested tabs → screens → widgets tree.
{
"theme": {
"primary": "#007AFF",
"secondary": "#5856D6",
"background": "#FFFFFF",
"text": "#000000"
},
"header": {
"title": "Health Research App",
"showBackButton": false,
"showSettings": true
},
"tabs": [
{
"id": "dashboard",
"label": "Dashboard",
"icon": "📊",
"screens": [
{
"id": "overview",
"title": "Overview",
"widgets": [
{
"id": "daily-check",
"type": "questionnaire",
"title": "Daily Health Check",
"priority": 1,
"config": {
"questions": [
{ "id": "sleep", "question": "How many hours did you sleep?", "type": "number", "min": 0, "max": 24 },
{ "id": "mood", "question": "How is your mood today?", "type": "scale", "min": 1, "max": 10 }
]
}
}
]
}
]
}
]
}LocalFileStrategy— bundle a JSON config with the app (Phase 1, what thestarter-kituses today).RemoteUrlStrategy— fetch a JSON config from any HTTPS URL.ServerStrategy— fetch project configuration from a RADAR-Base appserver (Phase 2 target).
All strategies return the same validated AppConfig, so swapping strategies is a one-line change in the host.
| Type | Component | Notes |
|---|---|---|
questionnaire |
QuestionnaireWidget |
Multi-format surveys |
task-list |
TaskListWidget |
Task tracking with status |
dashboard |
DashboardWidget |
Config-driven charts (inline values, API source, range pills) |
device-status |
DeviceStatusWidget |
Wearable / sensor connection status |
calendar |
CalendarWidget |
Schedule of tasks / events |
The repo is laid out as a library + a sibling consumer app for fast feedback.
./scripts/dev.sh install # install library + starter-kit deps
./scripts/dev.sh build # compile src/ -> lib/
./scripts/dev.sh typecheck # tsc --noEmit on the library
./scripts/dev.sh starter # run the starter-kit (Expo)
./scripts/dev.sh clean # remove lib/Equivalent npm scripts at the repo root:
npm run build # tsc
npm run typecheck # tsc --noEmit
npm run clean # rm -rf lib
npm run prepublishOnly # clean + build (runs automatically on `npm publish`)- Edit files under
src/. - Run
npm run buildat the repo root to refreshlib/. - The
starter-kit/pins"@radarbase/app-kit": "^1.0.0". While the library is unpublished, point the starter at the workspace via either"@radarbase/app-kit": "file:../"(temporary edit, don't commit) ornpm link— seestarter-kit/README.md.
cd starter-kit && npm start(or./scripts/dev.sh starterfrom root).- Edit
starter-kit/App.tsxandstarter-kit/config/*.jsonto experiment with new configs / widgets.
prepublishOnly runs clean && build so that npm publish ships a fresh lib/ and lib/index.d.ts. Only the lib/ directory and README.md are included in the published tarball (see the files field in package.json).
MIT — see LICENSE.
- Fork the repository.
- Create a feature branch.
- Make your changes under
src/and add a usage example instarter-kit/if relevant. - Run
npm run typecheckandnpm run buildat the repo root before opening a PR. - Submit a pull request.
For questions and support, please open an issue on GitHub.