• Getting Started
    • Overview
    • Why Without JSX?
    • Installation
    • Usage
    • Styling
    • Theming
    • Portal System
    • Rules & Patterns
    • Framework Integration
    • Compiler
    • FAQ
    • Release Notes
  • MUI Integration
  • Components
  • Hooks

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 MeoTheme puts 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

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/ui using Yarn or npm.
  • Usage: Learn the fundamental principles and common patterns for building user interfaces with @meonode/ui.
  • Styling: Advanced styling with css prop, 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/ui with 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