Native and web

The same components use a shared renderer interface on native platforms and in browsers. Every example on this site runs a real @ng-native/components component through the browser renderer, not a screenshot or a React port.

Controls have one implementation: packages/components/src/switch.ts, for example, serves both hosts.

The seam

HostEngine and HostNode in @ng-native/fabric define only the renderer calls @ng-native/components uses. Direct dependencies on inject(Engine), whose constructor requires a FabricUIManager, and Fabric's EngineNode would couple components to native rendering. Casting a browser object through as unknown as Engine would bypass type checking.

Fabric's Engine implements HostEngine; EngineNode extends HostNode. BrowserEngine implements both over the DOM without casts, while the native path stays unchanged.

Mounting on the web

import { provideRouter } from '@angular/router';
import { mount } from '@ng-native/web';
import { App } from './app/app.ts';

const root = document.getElementById('app-root')!;
mount(root, App, { providers: [provideRouter(routes)] });

Browser mount differs from @ng-native/platform's mount only in host requirements.

The first argument is the target Element. Fabric instead uses the numeric root tag supplied by React Native's host.

The browser needs no FabricUIManager, processColor or device tokens. It provides HostEngine, not Engine, so the bases of worklet-style, worklet-scroll and native-gesture throw NullInjectorError naming Engine. Their public directives fail earlier: their Reanimated and gesture-handler imports cannot load in a browser.

DOCUMENT is the real document, rather than native mount's TransferState stub.

The layout reset

Browser and Yoga layout defaults differ.

flex-row on a <view> sets flex-direction explicitly and behaves identically on both platforms. Without flex properties, Yoga defaults to display: flex; flex-direction: column; align-items: stretch; flex-shrink: 0, while browsers give the unknown <view> element display: inline; flex-shrink: 1.

@ng-native/web's reset.css aligns those defaults using [data-rn], which marks every created node. Tag selectors would miss text-input and switch, rendered as <textarea> and <input>.

<text> is excluded from the flex reset. React Native's Text supports wrapping and nested <text> inline runs; display: flex would prevent wrapping. The browser uses display: inline, which wraps but ignores explicit width and height. Native Text is a full Yoga node and accepts both. For explicit sizing on the web, add class="block" yourself.

What does not survive the trip

@ng-native/router uses react-native-screens and has no web build. <native-header> becomes an inert, invisible box; no native stack outlet exists. The browser host uses Angular's <router-outlet>, whose DOM-independent ViewContainerRef implementation works unchanged over BrowserRenderer.

Reanimated worklets and react-native-gesture-handler are native-only: their modules fail to load in browsers, and their bases fail through the injector.

Other controls work through the host. A self-focusing <text-input> and an offset-scrolling <scroll-view> use engine.measure() and dispatchCommand(), which BrowserEngine implements like Fabric's Engine. The site's examples exercise those implementations.

Why the web host exists

The web host tests whether the shared interface supports another implementation, rather than merely describing Fabric: running the same component against a different host exposes bugs that only one host would hide. It is not intended as a deployment target.

It also powers this site's examples: each calls mount into a page container, using the same source, cascade and press semantics as native.

In an existing Angular web app, <ng-native-island> embeds these components while sharing services and change detection. Reuse a mobile screen without a second implementation, but keep the native router and other native-only features on the phone. See Islands.

Conversely, <dom-component> renders a browser Angular component inside a native screen's web view, with inputs and outputs bound from the native template. Use it for canvas, charting libraries or editors. Its runtime is separate, so only JSON crosses. See DOM components.

Angular components as native iOS and Android views, built and shipped with Expo.

An alpha. MIT licensed. Sponsor its development.

An independent project, not affiliated with or endorsed by Google, the Angular team or Expo. Angular is a trademark of Google LLC.