@nannier-com/canvas
v2.62.3
Published
Universal React Native UI kit: one component API renders natively on iOS, Android, and web.
Maintainers
Readme
@nannier-com/canvas
One universal React Native UI kit that renders natively on iOS and Android, and on the web through React Native Web, from a single component API.

Try it live: the component catalog renders the full library (atoms, molecules, organisms, and charts) in the browser, with dark mode, the glass surface, and density switchable site-wide.
Write your screen once and it runs everywhere. Canvas is built from React Native primitives, React Native SVG, and its own components, so the same tree renders on device and in the browser with each platform's skin. Components are styled with semantic boolean props, are accessible by default (roles and state exposed to assistive tech on all three platforms), and are authored desktop-first so they scale down cleanly to phone. On iOS 26 the functional layer (overlays and bars) renders in real Liquid Glass; on Chromium browsers it renders as a real lens (an SVG displacement filter that refracts the backdrop at the rim); elsewhere it falls back to a genuine frost or a solid surface.
Install
npm install @nannier-com/canvasCanvas ships compiled (no build step in your app) and declares three required peer dependencies you install alongside it:
npm install react react-native react-native-svgOptional peers
Install the optional peers needed by your app's features. The package remains usable without them, with the following fallbacks:
| Package | Install only if | Without it |
| --- | --- | --- |
| react-native-qrcode-svg | you render QR codes with QRCode | the accessible, sized frame remains empty and warns once in development |
| expo-glass-effect | you want native Liquid Glass on supported iOS 26+ devices | forced glass uses expo-blur when available, otherwise the skin's solid fill; automatic surface mode remains solid |
| expo-blur | you want frosted glass on non-Chromium web, Android, or older iOS | surfaces use their solid fill when no other material is available; native Liquid Glass and the Chromium lens still work |
| expo-clipboard | you want CodeBlock to copy text on native | web can use navigator.clipboard; native copying needs a supplied onCopy handler |
| react-native-safe-area-context | you want safe-area insets in Canvas shells, with your app's SafeAreaProvider | safe-area wrappers render as plain views without insets |
| @shopify/react-native-skia | you want Backdrop to use an available GPU drawing backend | Backdrop keeps its React Native SVG renderer |
# add any subset you actually use
npm install react-native-qrcode-svg expo-glass-effect expo-blur expo-clipboard react-native-safe-area-context @shopify/react-native-skiaQuick start
Wrap your app once in ThemeProvider, then compose components imported from @nannier-com/canvas. The provider supplies the active color scheme, surface, and token map to every component below it.
import { ThemeProvider, Card, CardHeader, CardTitle, CardContent, Button } from "@nannier-com/canvas";
export default function App() {
return (
<ThemeProvider>
<Card padded>
<CardHeader>
<CardTitle>Welcome to Canvas</CardTitle>
</CardHeader>
<CardContent>
<Button primary large onPress={() => console.log("saved")}>
Save
</Button>
</CardContent>
</Card>
</ThemeProvider>
);
}That exact tree renders natively on iOS and Android and, through React Native Web, in the browser. There is no separate web component set to learn.
Styling with semantic boolean props
Every visual variation is a flat boolean prop named for its meaning; passing the prop turns it on, so the call site reads like natural language.
<Button primary large>Save</Button>
<Button destructive>Delete</Button>
<Button ghost small>Cancel</Button>
<Badge success>Active</Badge>
<Card raised selected>...</Card>Props are grouped into orthogonal axes (intent, size, density, plus stacking state and layout flags). Props on different axes combine freely; within one axis they are mutually exclusive, so you pass at most one and the component resolves any conflict by a fixed precedence.
// Four props from four axes, all applied together.
<Button primary large loading block>Save</Button>String-valued enum props such as variant="primary", size="lg", or tone="destructive" are not part of the API and are not accepted. The boolean form is the only styling surface, and there is no style-override escape hatch.
Platforms
Canvas targets all three platforms from one install. The only thing that changes is which peers your app already provides.
- Expo apps use the required peers and any optional modules needed by their features. The docs app in this repository is a working Expo example, including native glass and blur modules.
- Bare React Native works the same way once the required peers are installed and linked (
react-native-svgneeds the usual autolinking / pod install). Add the optional peers if you want QRCode or full-fidelity glass. - Web via React Native Web needs one bundler step: install
react-native-weband aliasreact-nativetoreact-native-web, exactly as any RNW project does. Web bundlers use the defaultdist/index.jsentry. On the web you can also flip glass at runtime with the exportedsetSurface("glass")/setSurface("solid")DOM helper.
Metro selects dist/native/index.js through the react-native export condition
or legacy field. That compiled output preserves Metro's iOS and Android module
selection, including native material helpers. No custom Canvas resolver is needed
in consumer apps. Public declarations and the web ESM build remain in dist/.
bun run dev watches both outputs and syncs them to local consumer overlays.
bun run verify-native-consumer packs the build and bundles an isolated React
Native consumer for both platforms with all optional peers omitted. CI also runs
this check on the exact sealed tarball before it can be published.
Theming
ThemeProvider reads the OS color scheme by default and exposes the resolved tokens to every Canvas component through the useTheme hook. Optional props control it:
dark/light(booleans): the scheme axis, spelled like every other Canvas axis (darkwins if both are passed). Omit both to follow the OS appearance. The legacyschemevalue prop ("light" | "dark") stays supported for code that already holds a scheme value.glass/solid(booleans): the surface axis, spelled like every other Canvas axis. Omit both for the platform default (Liquid Glass on iOS 26+, solid everywhere else); passglassto force the translucent functional-layer material,solidto force flat.glasswins if both are passed. The legacysurfacevalue prop ("solid" | "glass") stays supported for config-driven code holding aSurfacevalue.tokens: brand token overrides merged over the active scheme, so you can rebrand without forking the token files. Pass a flatPartial<ColorTokens>to apply to both schemes, or{ light, dark }to override each separately. Use a stable reference (a module constant or memoized object).
const brand = { primary: "#7c3aed" };
<ThemeProvider dark glass tokens={brand}>
<App />
</ThemeProvider>Documentation and components
Full docs, live examples, and the complete prop reference live at https://canvas.nannier.com/.
DESIGN.md ships with the package: the token values, the type roles,
the shape and elevation scales, and the four API rules that are easy to break by
accident. It is written to be read by an agent building on the kit as much as by a
person, and its numbers are generated from the kit's own sources.
The kit exports components across atoms, molecules, organisms, and charts, all from @nannier-com/canvas:
- Forms and inputs: Button, Button Group, Input, Textarea, Checkbox, Radio, Switch, Slider, Stepper, Input OTP, Select, Autocomplete, Listbox.
- Overlays: Dialog, Alert Dialog, Drawer, Popover, Tooltip, Dropdown, Action Sheet, Toast, Command palette.
- Navigation: Tabs, Tab Bar, Navbars, Sidebar, Breadcrumb, Pagination, Steps.
- Data and content: Data Table, Stacked / Grid Lists, Stats, Calendar, Charts, Card, Avatar, Badge, Description Lists, Media Objects, QR Code.
- Disclosure and feedback: Accordion, Collapsible, Carousel, Progress, Skeleton, Spinner, Alert, Empty State.
Alongside the components, the package exports the style foundation: the theme runtime (ThemeProvider, useTheme), the design tokens (token, hsl), the responsive and motion helpers (useResponsive, useReducedMotion), the glass helpers (liquidGlassAvailable, setSurface), and React Native primitives (View, Text, Pressable, TextInput, ScrollView). Image is a Canvas atom with semantic fit props.
Contributing
Contributions are welcome. CONTRIBUTING.md covers the Bun-based setup, the docs app you develop against, the check battery, and the design principles (semantic boolean props, no styling escape hatches, React Native everywhere) that contributions are reviewed against.
Security
Please report vulnerabilities privately; see SECURITY.md. Do not open public issues for security problems.
License
The compiled package distributed on npm as @nannier-com/canvas is licensed under
MIT. Its tarball includes the license and copyright notice.
The source repository is not covered by that grant and remains all rights reserved. These are separate terms for the distributed package and the repository source.
© 2026 Bobby Nannier.
