Skip to content
Plugins now load on macOS and the Apple Watch

Real native views.
No WebView anywhere.

Write Angular. Get a UIView, an android.view.View, an NSView or a SwiftUI view — the real control, from the real system, on eight platforms.
npx @angular/cli@latest new my-appcd my-app && an init

25 primitives · 8 platforms · one Rust core

The whole argument

One template. Whatever that platform actually ships.

Nothing here is drawn to resemble a control. The template does not change between one platform and the next — what changes is the class the framework mounts, and where a platform has no equivalent, nothing is mounted and the host says why.

<an-view [style.gap]="'16'">
<an-text [fontSize]="28">Alerts</an-text>
<an-switch
[on]="alerts()"
(onChange)="alerts.set($event)" />
</an-view>

an-switch becomes

UISwitch

The system's own switch: its animation, its haptic, its accessibility trait, and whatever it looks like in the next release of iOS.

MaterialSwitch

Material 3, resolved without Gradle. It follows the device theme, including the dynamic colour a user picked from their wallpaper.

NSSwitch

AppKit, in a window that resizes — with a pointer that has a shape and a hover state, both of which a finger does not have.

Toggle

The one host that is not a view hierarchy: the tree is mirrored into a model SwiftUI redraws.

UISwitch

The same host as the phone, in a windowed scene. A window in space is not a screen, but it is still UIKit.

MaterialSwitch

The phone's host on a round screen, where the safe area is how the bezel's margin arrives.

nothing is mounted

UISwitch is not in the tvOS SDK — a television is driven with a remote. The layout keeps the gap it had measured and the host says so once, in the log.

Real controls, not lookalikes

Nothing is drawn to resemble a control. The switch in your template is the system’s switch, so it looks and behaves however that control does in that version of the OS, and it ages with the platform instead of with this repository.

  • iOS · iPadOSUISwitch
  • AndroidMaterialSwitch
  • macOSNSSwitch
  • watchOSToggle
  • visionOSUISwitch
  • tvOSnot in the SDK

The layout is not the platform’s

UIKit, AppKit and Android lay out differently, and SwiftUI does not let you lay out at all — so layout leaves the platform entirely. Flexbox runs once, in a Rust core over taffy, and every host is handed absolute frames in logical points. The same template lays out identically on a phone, a TV and a watch.

Angular as it is

Signals, @if, @for, the router with parameters, AOT templates, dependency injection. Zoneless, and not optionally: without zone.js there are no timers or XHR to patch inside the engine, which is most of what makes this painful elsewhere. No dialect, no subset.

A gap is never silent

A prop no host reads, a style nobody recognises, an event a platform cannot deliver, a plugin missing a platform: each one warns, or stops the build. A primitive that is unsupported somewhere has to say so in writing, and scripts enforce it.

[angular-native] (pinch) does not arrive on watchOS:
  MagnifyGesture is @available(watchOS, unavailable),
  and two fingers do not fit on a 40 mm screen

Recorded on a simulator, not mocked up

Two apps out of examples/, running.

Both of these are ordinary Angular components — signals, @for, @if — and every control in them is the platform's own. The recordings are the iOS simulator, driven by hand: the search field is a UISearchBar, the size picker is a UISegmentedControl, the quantity is a UIStepper, and the stars are SF Symbols.

A gallery that reflows

Two columns dealt out by running height, a system search field, a category strip and pull-to-refresh. There is no grid in the layout engine and none is needed: a waterfall is two flex columns, resolved once in Rust.

  • an-search-bar
  • an-segmented-control
  • an-image
  • an-scroll-view
How long lists work

A product page you can swipe

The carousel is the one thing here that is not a system control, because no platform ships one — so it is a pan gesture moving a transform, and letting go hands the rest to the platform’s own drawing thread. The stepper and the switch beside it are UIKit’s.

  • an-stepper
  • an-switch
  • an-segmented-control
  • (pan)
  • [animate]
Gestures and animation

Where it runs

Eight platforms, one bundle

The same bundle on all of them. What differs is not the code — it is which controls that platform ships, and every gap is listed with the reason for it.

  • iOS · iPadOS

    Renders withUIKit

    All 25 primitives

  • Android

    Renders withandroid.view + Material 3

    All 25 primitives

  • macOS

    Renders withAppKit

    All 25 primitives

  • tvOS

    Renders withUIKit + focus engine

    20 of 25 — a TV has no switch or slider

  • visionOS

    Renders withUIKit in a windowed scene

    All 25 primitives

  • watchOS

    Renders withSwiftUI

    17 of 25

  • Wear OS

    Renders withandroid.view

    18 of 25

  • HarmonyOS · Windows

    Renders with

    Not started

In one minute

Four commands on a project you already had

An Angular CLI project, unmodified, running on native views. Nothing moves into this repository and ng build keeps working.

an init
Dependencies, tsconfig and an entry point. It touches neither src/main.ts nor angular.json.
an add ios
Writes ios/Info.plist, yours from then on.
an ios
Builds, assembles the .app without Xcode and launches it in the simulator.
Terminal window
npx @angular/cli@latest new my-app && cd my-app
an init
an add ios
an ios

Write Angular. Ship native.

An ordinary ng new project, four commands, and it is running on real UIKit and real Material views.

Quick start