Announcing lynx-ui

All Posts
May 22, 2026
Zhou Fang
Zhou FangEngineering @ Lynx
Ryan Wang
Ryan WangEngineering @ Lynx
Adrian Luo
Adrian LuoEngineering @ Lynx
MoonfaceX
MoonfaceXComponent Lead @ Lynx
Guangyu Du
Guangyu DuDesign Engineering @ Lynx
Xuan Huang
Xuan HuangArchitect @ Lynx

lynx-ui has been quietly open-sourced since we gave an early preview at React Advanced London last December. After months of polishing components, building the documentation site, and adding examples, today we are excited to announce it as generally available.

lynx-ui components at a glance

Native Foundations, Web Velocity

Built-in elements and XElement give Lynx the low-level capabilities that matter: fast scrolling, precise gestures, and native rendering. But products are not built from atoms alone. That is where components belong. At Lynx, native provides the foundation; the web-inspired frontend stack provides the extension layer for composition, state, styling, and product-specific behavior.

lynx-ui delivers on this layering principle: preserve native user experience, deliver the velocity of the web.

lynx-ui's ScrollView is built on top of the native <scroll-view> primitive

Composable Primitives

Encapsulation can become a trap. lynx-ui does not treat components as black boxes with endlessly growing prop surfaces. Instead, it is an unstyled component library where each component is a primitive that handles behavior and state, but ships with no visual design. A primitive is composed of parts that you render and style independently.

Popover shows the idea. It is not a single opaque component, but a composition of parts with separate responsibilities: PopoverRoot owns state, PopoverTrigger defines intent, PopoverPositioner handles placement, and PopoverContent stays structurally independent.

Composition is all you need.

Because primitives are unstyled, the same composable model creates a foundation for design systems. You can define your own themes through the styling and theming system, or follow the shadcn/ui pattern to compose themed wrappers on top of the primitives.

LUNA

LUNA is a reference design language we built on top of that foundation.

It grows from Lynx's signature gradient, adapting the brand expression into a softer palette for UI: warm pinks and rose tones moving into lavender and aqua. In LUNA, gradients act as first-class visual materials for expressing form and movement, giving components a sense of changing mediums. That sensitivity also carries into token names: LUNA describes how surfaces are felt, not where they are placed. ambient dissolves into the background, faint sits near the threshold of perception, veil and film create soft translucent layers.

LUNA Studio

Programmable Interaction

Components built around continuous input need two things at once: highly customizable behavior and low-latency response. A swipe, drag, or snap is judged frame by frame. If the visual response trails behind the gesture, the component may still work, but it no longer feels native.

Lynx's dual-thread model defaults JavaScript to the background thread, keeping the main thread free for rendering. But Main-Thread Script can move interaction logic onto the main thread when it matters, making per-frame behavior fully programmable without crossing a thread boundary. Swiper shows the idea directly: drag input feeds transform updates frame by frame, so the card follows the gesture instead of catching up to it later.

A 'main thread' function passed to main-thread:customAnimation runs per-frame logic on the main thread

With native foundations and programmable interaction in place, motion becomes practical, not just decorative.

Motion

Motion is one of the most popular animation libraries in the Web community, supporting both spring-based and bezier-based transitions. Thanks to the programmability that Main-Thread Script brings, we adapted Motion for Lynx as @lynx-js/motion, a thin layer that reuses Motion's source directly, and ship an experimental version together with lynx-ui. If you know Motion on the web, you already know how to use it in Lynx.

Sheet shows why that matters. Drag, resistance, release, and settling need to read as one continuous behavior. With Main-Thread Script keeping interaction close to rendering and Motion defining how the surface moves, the sheet feels held, not just played back.

Sheet uses @lynx-js/motion under the hood for spring-driven drag and settle transitions

Try it today

lynx-ui is available on iOS, Android, and HarmonyOS today, with partial Web and Desktop support on the horizon. Read the introduction to get oriented, or jump straight to the quick start to install @lynx-js/lynx-ui-*.

To see the components in action, grab Lynx Explorer, scan the QR codes in the component demos, and see them running right away. Or bring it into your own Lynx app and try it in a real product flow.

We want lynx-ui to grow from the way real apps are built. If it feels great, gets in your way, or leaves something missing, let us know in the lynx-family/lynx-ui repository. Help shape what comes next.

Except as otherwise noted, this work is licensed under a Creative Commons Attribution 4.0 International License, and code samples are licensed under the Apache License 2.0.