Native views
Most Expo modules need nothing from this package: requireNativeModule, EventEmitter,
SharedObject and SharedRef have no React in them, so a module that only calls into native is
imported and used as its own documentation says. What needs something is a module that renders.
Its React wrapper reaches the Fabric component through requireNativeViewManager, which caches a
host component, builds a view config for React's renderer, and derives the Fabric component's
name from the module name. None of the React parts are reachable without React, and none of them
are needed - the engine writes props onto the shadow node itself, and Expo's Fabric views take
their props as an untyped map, so there is no codegen'd prop list to satisfy. What is left, and
what this page is about, is the name.
import { SegmentedControl } from '@ng-native/expo';
<segmented-control> once SegmentedControl is in the component's importsThese are all on the bare @ng-native/expo import - none of them is bound to one optional module.
import {
registerExpoView,
registerExpoViews,
registerNativeViews,
expoViewName,
} from '@ng-native/expo';The smallest thing that works
import { registerExpoViews, registerNativeViews } from '@ng-native/expo';
registerExpoViews('expo-image', 'expo-blur'); // once, before the app mounts
registerNativeViews('web-view', 'slider');<expo-image [source]="[{ uri }]" contentFit="cover" [transition]="{ duration: 400 }" /><expo-image> is then an element like any other, and its props are the ones the module's native
view declares - the ones the React component would have passed down, after whatever resolving it
does in JavaScript.
Expo's own views
registerExpoView(elementName, moduleName, options?) teaches the engine an element name for
one Expo module's view: registerExpoView('expo-image', 'ExpoImage') makes <expo-image> commit
as one. Call it at startup, before the first commit that uses the element - the app still has to
have the module installed so the native side registers the component; this only does the
JavaScript half.
options.viewName names one of several views a module has (the default view, the module's
first, takes no name); options.defaultProps are props the module's React component would have
applied before native saw them - expo-image resolving a contentFit string, for instance.
registerExpoViews(...elements) registers several known views at once, by element name:
registerExpoViews('expo-image', 'expo-blur', 'expo-camera');EXPO_VIEWS is the table it reads from - the views worth knowing the names of, so an app does not
have to look each one up:
| Element | Module view | Notes |
|---|---|---|
expo-image |
ExpoImage |
Takes source as a list; a single { uri } is wrapped by the caller. |
expo-blur |
ExpoBlurView |
A blur of what is behind a view - the one thing CSS cannot express. |
expo-video |
ExpoVideo (VideoView) |
The player is a shared object the module hands out; this is its view. |
expo-camera |
ExpoCamera |
The module's default view. See Camera. |
expo-symbol |
SymbolModule |
SF Symbols, iOS only. |
expo-gl |
ExpoGL |
Its context is reached through an event rather than a prop. |
expo-glass |
ExpoGlassEffect (GlassView) |
The iOS 26 material. Renders as a plain view where unavailable. |
expo-mesh-gradient |
ExpoMeshGradient (MeshGradientView) |
The one gradient with no CSS spelling. |
expo-live-photo |
ExpoLivePhoto (LivePhotoView) |
iOS only. |
expo-maps-google / expo-maps-apple |
ExpoGoogleMaps / ExpoAppleMaps |
Two modules, one view each. Typed as one: Maps. |
Registering one you have not installed does not error at registration time; it produces an element
that commits as nothing (UnimplementedNativeView), which is why elements are named one at a time
rather than all at once - finding out at startup which modules are actually present is not
something JavaScript can do without importing them all.
Community views
Not every native view an Expo app reaches for is an Expo module. NATIVE_VIEWS and
registerNativeViews(...elements) cover the libraries in Expo's own bundled module list, whose
Fabric names are their own - whatever codegen produced from each library's spec - rather than the
ViewManagerAdapter_ names registerExpoView derives:
| Element | Library | Notes |
|---|---|---|
web-view |
react-native-webview |
|
slider |
@react-native-community/slider |
|
date-time-picker |
@react-native-community/datetimepicker |
Android renders a dialog, iOS an inline view. |
picker / picker-item |
@react-native-picker/picker |
Items are <picker-item> children. |
segmented-control |
@react-native-segmented-control/segmented-control |
iOS only. See below. |
masked-view |
@react-native-masked-view/masked-view |
maskElement is a prop, not a child. |
pager-view |
react-native-pager-view |
The swipeable pager a tab layout is built on. |
lottie-view |
lottie-react-native |
|
skia-view |
@shopify/react-native-skia |
Takes an imperative picture; declarative Skia elements do not come across. |
view-shot |
react-native-view-shot |
Captures whatever it wraps. |
segmented-control is a view manager from before Fabric, run through the interop layer, which
only installs its onChange native block when the prop is set - as React's handler does by being
there. Bind (change) or nothing fires. SegmentedControl (segmented-control.ts) is a thin
typed component over this element for strict templates: import it and keep registering the name -
the component supplies the types, the registration is what makes the element commit.
skia-view's name is right, but worth being plain about: Skia's drawing model is React elements
through a reconciler of its own, and none of that is reachable here. What the element takes is a
picture, built with Skia's imperative PictureRecorder API. Anything drawn declaratively does
not come across, and a component that renders nothing is what that looks like.
Deriving the name yourself
expoViewName(moduleName, viewName?) computes the exact Fabric component name
requireNativeViewManager would, for a module registerExpoView does not already know about:
ViewManagerAdapter_<moduleName> (or ViewManagerAdapter_<moduleName>_<viewName> for a named
view), plus a per-app suffix. The suffix is the part that cannot be hardcoded: Expo Go runs many
projects in one binary, so it namespaces every view name with a per-app identifier; a standalone
build has none. Get it wrong and the view commits as UnimplementedNativeView with no error - the
same failure mode as a misspelled name. registerExpoView calls this for you; reach for it
directly only if you are registering a name some other way.
Without the module
An element registered for a module or library that is not installed commits as nothing
(UnimplementedNativeView) rather than throwing - the same failure mode as a misspelled element
name, so check the name and the install first.
Reference
segmented-controlcomponent@ng-native/expoimport { SegmentedControl } from '@ng-native/expo';and add SegmentedControl to the component's importsInputs
valuesreadonly string[]selectedIndexnumberenabledbooleanmomentarybooleanSegments do not stay selected: each is a button.
tintColorstringThe selected segment's colour.
backgroundColorstringfontStyleSegmentedControlFontactiveFontStyleSegmentedControlFontapportionsSegmentWidthsByContentbooleanEach segment as wide as its title rather than all equal.
accessibilityLabelstringOutputs
changeSegmentedControlChangeEvent