Quick start
This walks the shortest path from nothing to a working round trip: define a contract, generate the native bindings, implement it natively (Kotlin or Swift), consume it from React Native.
-
Define a contract in a
*.contract.tsfile. Import only from the pure@malopezr7/bridgekit/contractentrypoint.app-host.contract.ts import { defineContract, t } from '@malopezr7/bridgekit/contract';export const AppHost = defineContract('app.host', {methods: {isLoggedIn: t.query(t.boolean()),saveFile: t.query(t.object({ url: t.string(), name: t.string() }),t.literals('success', 'already-exists', 'cancelled', 'error'),{ timeoutMs: null },),},streams: { notifications: t.stream(t.string()) },state: { connectivity: t.state(t.object({ online: t.boolean() }), { online: false }) },}); -
Generate the native bindings with the BridgeKit CLI. Output is committed, readable code — no hidden directories. Pick the target platform with
--platform:Terminal window # Android (default platform is kotlin)bridgekit generate --contracts 'src/**/*.contract.ts' --out-dir bridgekit/generated# iOS — same contracts, Swift bindingsbridgekit generate --contracts 'src/**/*.contract.ts' --platform swift --out-dir ios/contracts/generated -
Implement the provider on the side that owns the capability. Here native owns it — implement the generated interface (Kotlin) or protocol (Swift):
class AppHostProvider : AppHost {override suspend fun isLoggedIn(): Boolean = session.isLoggedIn()override suspend fun saveFile(params: SaveFileParams): SaveFileResult = /* ... */override fun notifications(): Flow<String> = pushService.messages()override val connectivity = MutableStateFlow(Connectivity(online = true))}BridgeKit.default.provide(AppHostContract, Scope.Global) { AppHostProvider() }import BridgeKitfinal class AppHostProvider: AppHost {func isLoggedIn() throws -> Bool { session.isLoggedIn() }func saveFile(_ params: SaveFileParams) async throws -> String { /* ... */ }func notifications() -> AsyncStream<String> { pushService.messages() }var connectivity: AsyncStream<Connectivity> { /* yield current + updates */ }}import BridgeKit_ = BridgeKitRuntime.default.provide(AppHostContract(), scope: .global) {AppHostProvider()} -
Consume it from React Native with a typed proxy and the state/stream hooks.
import { useBridge, useBridgeState } from '@malopezr7/bridgekit';function FileScreen() {const app = useBridge(AppHost);const { value: net } = useBridgeState(AppHost, 'connectivity');const onInstall = async () => {const result = await app.saveFile({ url, name });// result: 'success' | 'already-exists' | 'cancelled' | 'error'};return <Button disabled={!net.online} onPress={onInstall} />;} -
Subscribe to a stream — no
useEffectdance, no event-emitter names.const notes = app.notifications();const unsubscribe = notes.subscribe((note) => addNotification(note));// …or: for await (const note of notes) { ... }
The other direction in one line
Section titled “The other direction in one line”When RN provides and native consumes, the shape is identical — only the implementer moves:
useProvideBridge(ExampleFeature, { getUnreadCount: () => store.unread });val feature = bridgekit.consume(ExampleFeatureContract)val count = feature.getUnreadCount()feature.sessionStatus.collect { value -> /* BridgeValue<SessionStatus> */ }Next: wire BridgeKit into a real project in Installation, understand the building blocks in Defining a contract, or see a complete real app in the demo walkthrough.