Overview — MeoNode UI
Type-safe React components without JSX
A production-ready component library that replaces JSX with function-based composition, featuring direct CSS prop styling, context-based theming, opt-in memoization, and React Server Components support on Next.js—powered by @emotion/react.
Core Concept
MeoNode UI eliminates JSX while maintaining full React compatibility through functional composition. Style components with CSS properties as props, leverage automatic theme resolution via React Context, and memoize any node you choose by handing it a dependency array.
JSX Pattern:
<div style={{ padding: '20px', borderRadius: '12px' }}> <h2 style={{ fontSize: '1.5rem', marginBottom: '8px' }}>{title}</h2> {children} </div>
MeoNode Pattern:
Div({ padding: '20px', borderRadius: '12px', backgroundColor: 'theme.primary', children: [H2(title, { fontSize: '1.5rem', marginBottom: '8px' }), children], })
children goes in as its own member rather than being spread. A nested array is
a list in its own right, so React checks the caller's rows for keys the way it
would inside JSX, while the heading beside them is a sibling you wrote out and is
never asked for one. Spreading flattens both into a single list, and the heading
is then asked for a key it does not need. See
lists and keys.
Nesting requires @meonode/ui 2.2.1 — 2.2.0 renders it but does not typecheck it. Before that a children array could not
hold arrays, and spreading was the only way to write this.
Features
Function-Based Composition
Plain JavaScript functions replace JSX—no build transforms, no syntax extensions. Compose UIs as first-class function trees with full TypeScript inference.
Direct CSS-in-Props
Pass CSS properties directly to components. No separate styled-components declarations, no className juggling. All valid CSS properties work as props with full type safety.
Button('Submit', { padding: '12px 24px', backgroundColor: 'theme.primary', borderRadius: 8, transition: 'all 0.2s ease', css: { ':hover': { transform: 'scale(1.05)' }, }, })
Element Polymorphism (as)
Swap the rendered HTML tag while keeping styles and Emotion classes — similar to Emotion's as prop. TypeScript narrows DOM props and handlers to the resolved tag.
Div({ as: 'a', href: '/home', backgroundColor: 'theme.primary', padding: '12px 24px', children: 'Go home', })
See the Styling guide for factory coverage, limitations, and RSC notes.
Context-Based Theming
Theme values resolve automatically through React Context. Reference semantic tokens anywhere without prop drilling. Supports nested themes and dynamic switching.
Token values are var() references, so one token object serves every mode. The server therefore sends the same
document to every reader — it does not need to know which mode anyone is in — and the mode is applied by a
data-theme attribute on <html>, written before the first paint. See the
Theming guide.
ThemeProvider({ modes: ['light', 'dark'], defaultMode: 'light', tokens: { primary: { default: 'var(--primary-default)', content: 'var(--primary-content)' }, spacing: { sm: '8px', md: '16px', lg: '24px' }, }, children: [ /* your app */ ], })
Surgical Memoization
Memoize at node-level granularity—not just entire components. Control re-renders with precision by specifying dependency arrays directly on individual nodes.
Node-Level Memoization:
const UserCard = ({ user }) => Div( { padding: 16, children: [ H2(user.name), Text(user.bio), ], }, [user.id], ).render() // The whole subtree rebuilds only when user.id changes
Component-Level Memoization:
Node(ExpensiveComponent, { data }, [data.id]).render()
React Server Components Compatible
Server and client component boundaries work as React defines them, and nodes
compose across both. On the Next.js App Router, server-rendered styles reach
the document through StyleRegistry from @meonode/ui/nextjs-registry, which is
built on Next.js's useServerInsertedHTML; RSC styling is supported there.
A server rendering with renderToString or renderToPipeableStream needs no
registry: the styles are written into the HTML it renders, and the client
hydrates with the same class names. Since 3.1.0 this includes components built
with createNode and Emotion or MUI components given styles; on 3.0 their class
names differed between server and client.
Emotion-Powered Styling
Built on @emotion/react for:
- Automatic critical CSS extraction
- Vendor prefixing
- Dead code elimination
- Server-side rendering support
Advanced Portal System
Managed, stack-based overlay system for modals, drawers, and tooltips. Features multi-layered portals, automatic state synchronization, and high-performance subscription-based updates via Data Channels.
Smart Prop Differentiation
Automatically separates style props from DOM attributes. Pass onClick, aria-*, data-* alongside CSS props—MeoNode
routes them correctly.
Button('Click Me', { padding: '12px', // → style color: 'theme.primary', // → style onClick: handleClick, // → DOM attribute 'aria-label': 'Submit', // → DOM attribute disabled: isLoading, // → DOM attribute })
Quick Start
A single-theme app declares one mode and never offers a switch — the smallest thing that works. Adding light and dark
is the same provider with another name in modes and the colours declared per mode in CSS; see
Theme Modes.
import { ThemeProvider, Center, Column, H1, Button, Text } from '@meonode/ui' const tokens = { primary: { default: '#FF6B6B', content: '#FFFFFF' }, base: { default: '#F8F8F8', content: '#333333' }, } const App = () => ThemeProvider({ modes: ['light'], defaultMode: 'light', tokens, children: [ Center({ padding: 40, backgroundColor: 'theme.base', children: Column({ gap: 24, children: [ H1('MeoNode UI', { fontSize: '3rem', color: 'theme.primary', }), Text('Type-safe React without JSX', { fontSize: '1.2rem', color: 'theme.base.content', }), Button('Get Started', { backgroundColor: 'theme.primary', color: 'theme.primary.content', padding: '12px 24px', borderRadius: 8, cursor: 'pointer', onClick: () => console.log('Started!'), }), ], }), }), ], }).render()
Architecture
Component Factory System
Node factory + Component wrapper + semantic elements (Div, Button, etc.) enable both rapid prototyping and
sophisticated component architectures.
Theme Resolution Engine
Context-based theme propagation with automatic value resolution. Nested theme objects inherit and override parent values
without explicit passing.
Advanced Portal System
Stack-based managed overlays with PortalProvider and PortalHost. Supports nested portals, automatic data
synchronization, and high-performance updates via subscription-based Data Channels.
CSS Engine
@emotion/react provides CSS-in-JS with critical CSS extraction for SSR and class-name deduplication, so a style
serialized once is reused wherever it repeats.
Why MeoNode UI?
For Teams Building Design Systems:
- Context-based theme propagation
- Semantic token system ensures visual consistency
- Component composition patterns scale naturally
- Augmenting
MeoThemeputs your token paths in autocomplete, and development names any that the theme does not define
For Performance-Critical Applications:
- Emotion's CSS optimization and caching
- Surgical memoization at node granularity
- SSR-ready with critical CSS extraction
- RSC compatibility for modern React architectures
For Developer Productivity:
- No JSX compilation overhead
- Direct CSS-in-props reduces context switching
- Intelligent prop routing (style vs DOM attributes)
- Full autocomplete for all CSS properties
- Composable function trees with first-class JavaScript
Related FAQ
What is MeoNode UI?
MeoNode UI is a type-safe React UI library that replaces JSX with function-based composition and supports direct CSS props, theming, and React Server Components.
Why choose MeoNode UI over traditional JSX?
It keeps UI code in plain TypeScript function calls, improves prop inference ergonomics, and removes JSX transform overhead while staying compatible with React libraries.
How do I get started quickly?
Install @meonode/ui, wrap your app with your providers, and render your root node. Then continue with the Installation and Usage guides.
More details: /docs/getting-started/faq
Next Steps
Ready to build React applications without JSX?
- Installation: Step-by-step guide to installing
@meonode/uiusing Yarn or npm. - Usage: Learn the fundamental principles and common patterns for building user interfaces with
@meonode/ui. - Styling: Advanced styling with
cssprop, pseudo-classes, media queries, and animations. - Theming: Learn how to customize your design system with the Context-based theming system.
- Framework Integration: Learn how to integrate
@meonode/uiwith Next.js, Vite, and other React setups. - FAQ: Find answers to common questions and troubleshooting tips.
- Release Notes: Stay updated with the latest changes and improvements.
On this page
- Overview — MeoNode UI