expo-interface
v0.23.0
Published
Native-first universal UI kit for Expo — SwiftUI on iOS, Jetpack Compose on Android, real DOM on web, WinUI 3 on Windows — built on @expo/ui and react-native-windows.
Maintainers
Readme
Expo Interface
A UI kit for Expo where every component is the platform's own control.
Write a screen once. It renders as SwiftUI on iOS, Jetpack Compose (Material 3) on Android, real DOM elements on web, and WinUI 3 on Windows. One accent color themes all four, and screens follow the system's light or dark scheme with no per-platform styling.
import {Button, FieldGroup, Screen, Switch} from 'expo-interface';
export default function Settings() {
return (
<Screen native>
<FieldGroup>
<FieldGroup.Section title="Sync">
<Switch label="Notifications" value={on} onValueChange={setOn}/>
<Button label="Continue" onPress={save}/>
</FieldGroup.Section>
</FieldGroup>
</Screen>
);
}| Platform | That screen is | Through |
| --- | --- | --- |
| iOS | A SwiftUI Form with a Toggle and a Button | @expo/ui/swift-ui |
| Android | A Material 3 grouped list with a Switch and a Button | @expo/ui/jetpack-compose |
| Web | <button>, <input> and the rest, with CSS beside each component | react-native-web for layout |
| Windows | A Settings-style card with a ToggleSwitch and a Button | The kit's own WinUI library, on expo-windows |
See every component live in the Storybook.
Quick start
Requires Expo SDK 57 and Expo Router.
1. Install
npx expo install expo-interface @expo/ui @expo/material-symbolsThe peer dependencies are standard Expo modules that most Expo Router apps already have. If any are missing:
npx expo install expo-router expo-symbols expo-asset expo-image expo-constants expo-status-bar expo-system-ui expo-web-browser react-native-safe-area-context2. Wrap the app
Use AccentProvider and the kit's Stack in the root layout. seed is any
hex color, and the default is #007AFF.
// app/_layout.tsx
import {ThemeProvider} from 'expo-router';
import {AccentProvider, Stack, useNavTheme} from 'expo-interface';
function Navigation() {
return (
<ThemeProvider value={useNavTheme()}>
<Stack screenOptions={{headerShown: false}}/>
</ThemeProvider>
);
}
export default function Layout() {
return (
<AccentProvider seed="#8959EA">
<Navigation/>
</AccentProvider>
);
}3. Web only: emit the palette
// app/+html.tsx
import {ScrollViewStyleReset} from 'expo-router/html';
import {getThemeBootScript, getThemeCSS} from 'expo-interface';
export default function Root({children}: React.PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style dangerouslySetInnerHTML={{__html: getThemeCSS()}}/>
<script dangerouslySetInnerHTML={{__html: getThemeBootScript()}}/>
<ScrollViewStyleReset/>
</head>
<body>{children}</body>
</html>
);
}4. Android only: register XML assets
Jetpack Compose draws icons from XML vector drawables, which
@expo/material-symbols provides.
// metro.config.js
const {getDefaultConfig} = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.assetExts.push('xml');
module.exports = config;5. Build screens
Use the screen at the top of this page, then run the app on two platforms and compare. Getting started continues from here with tabs, icons and troubleshooting.
Windows
Add the platform with expo-windows. The kit needs no setup of its own there. See Windows.
Components
Everything is exported from expo-interface. Value controls are controlled:
pair value with onValueChange. Each link says what the component does, its
props, what each platform renders, and where the platforms differ.
| Group | Components |
| --- | --- |
| Layout | Screen, ScreenHeader, NativeHost, Surface, Card, Toolbar, KeyboardBar, FieldGroup, ListItem, Collapsible, Divider, EmptyState |
| Navigation | Stack, Tabs, TabStack, ConstrainedStackHeader, TabView, Pager, HeaderMenu, HeaderAction, HeaderActions, ExternalLink, ShareLink |
| Controls | Button, Fab, Chip, IconToggle, Switch, Checkbox, TextField, SearchField, Picker, SegmentedControl, Slider, Stepper, DateTimePicker, ColorPicker |
| Indicators | Progress, Spinner, Gauge, Badge, Avatar, Typography and its variants |
| Overlays | Menu, ContextMenu, PopupMenu, Popover, Tooltip, Alert, Sheet, Toast |
Features
| Feature | What it does | Guide |
| --- | --- | --- |
| One accent | AccentProvider takes one hex seed. It becomes the SwiftUI tint, a full Material 3 palette, CSS variables, and WinUI's accent brushes. | Theming |
| Light and dark | Every component follows the system. setColorScheme forces one, and on web the choice survives a reload without a flash. | Theming |
| Color tokens | theme.label, theme.background and the rest resolve to values the OS keeps current, with no re-render. useColor and usePalette give plain strings. | Theming |
| Icons | One token names the icon in SF Symbols, Material Symbols and Segoe Fluent Icons. fill: true asks for the solid form. | Icons |
| Native hosts | Screen native hosts a whole screen. NativeHost hosts a group of controls beside a canvas or an editor. | Native hosts |
| Navigation | Stack, Tabs and TabStack over Expo Router: the native stack and tab bar, a floating bar on web, a NavigationView on Windows. | Navigation |
| Web platform primitives | Popovers, anchor positioning, <dialog>, <select> and <datalist> rather than portals and z-index, with the ARIA keyboard patterns. | Web |
| Windows | WinUI 3 controls in XAML islands, content in the title bar, keyboard shortcuts, high contrast, right to left and scaling. | Windows |
| @expo/ui on Windows | Screens written against @expo/ui and the community controls draw WinUI controls with no change. | Windows |
| Accessibility | An accessible name on every control, headings, live regions, and the platform's own semantics. | Accessibility |
| Honest differences | A prop a platform cannot honour is documented as absent there. Nothing is faked. | Where platforms differ |
Documentation
| Page | What is in it | | --- | --- | | Getting started | The quick start in full, tabs, icons, troubleshooting. | | Components | Every component, one line each, linked to its entry. | | Theming | Accent, scheme, tokens, reading colors, high contrast, constants. | | Icons | Tokens, filled icons, drawables, the web font, Windows glyphs. | | Native hosts | Where native controls live on iOS and Android. | | Web and Windows | What each platform does its own way. | | Where platforms differ | The differences that change what a screen can do, in one table. | | Accessibility | What the kit sets on each platform. | | Contributing | The repository, the checks, Storybook, the harness, CI. |
Versions
Expo SDK 57, React Native 0.86.3 and @expo/ui 57.0.18. On Windows the kit
runs on expo-windows. React Native
0.86 has no react-native-windows release yet, so the Windows side is built and
tested on the 0.84 line.
expo-windows' versions page
says what that means for an app today.
Contributing
The example app, dropfiles, uses every component and imports the
package from ../src.
bun install # bun 1.4 or later
bun run web # or ios, android
bun run lint
bun run typecheck
bun run test # vitest, every platform project, 100% coverageContributing has the rest: how a component is built, the Storybooks, the tests, the harness and the Windows build.
License
MIT
