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

Why Build React without JSX?

JSX is convenient, well-supported, and the default for almost every React project. MeoNode UI doesn't claim it's broken — it just makes a different trade: express UI as plain function calls instead of a syntax extension, and lean on what TypeScript already does well.

This page lays out where that trade pays off, and where it doesn't.

What JSX gives you

  • An HTML-like syntax that maps cleanly to designer intuition
  • Direct compatibility with every React tutorial, course, and Stack Overflow answer ever written
  • A massive ecosystem of JSX-aware tools (Storybook addons, design-to-code generators, JSX-specific linters)

Tooling like Prettier, ESLint, and IDE folding works in both worlds — MeoNode is plain TypeScript, so all of it applies natively without special plugins. The JSX-exclusive ecosystem is the more concrete advantage.

If the items above matter most for your team, you don't need this library. JSX is a fine default.

What MeoNode trades it for

1. One language, one mental model

JSX requires moving between two contexts in the same file: an XML-like grammar for elements and standard JavaScript inside {} braces. The boundary is small but constant.

<div className="dashboard">
  {isLoggedIn ? (
    isAdmin ? <AdminDashboard /> : <UserDashboard />
  ) : (
    <LoginForm />
  )}
</div>
import { Div, Node } from '@meonode/ui'

Div({
  className: 'dashboard',
  children: isLoggedIn
    ? isAdmin
      ? Node(AdminDashboard)
      : Node(UserDashboard)
    : Node(LoginForm),
})

Same logic; the MeoNode version stays in expression position throughout. Whether that reads better is taste — but if you've ever found yourself writing IIFEs or extracting variables just to escape JSX context, it's a real ergonomics win.

2. Direct TypeScript inference

Node(Component, { ... }) is a normal function call, so TypeScript validates the props against the component's signature directly. JSX works too, but it routes through the JSX.IntrinsicElements / JSX.LibraryManagedAttributes machinery, which can be slower to error and harder to debug when something is off.

For most apps the difference is invisible. For libraries, generic components, or codebases where tsc performance matters, it can add up.

3. Skip the JSX transform

Without JSX, you can drop one transform from your build pipeline. You'll still need TypeScript (if you use it), bundling, and everything else — so calling this "no build step" would be misleading. But the toolchain is one piece smaller, and dev-server cold starts are slightly faster as a result.

If you're writing a tiny page that loads source as ESM directly in the browser, MeoNode does run with no build at all. That's the rare case, not the common one.

4. Styling and theming in the same call

In a typical JSX setup, styling lives in a separate layer — CSS modules, Tailwind, styled-components, emotion, or whatever the team picked. MeoNode bundles a styling and theming system into the node itself, powered by @emotion/react:

Div({
  color: 'theme.primary',   // resolved from ThemeProvider
  padding: 20,
  children: 'Hello',
})

Theme tokens resolve via context with no useTheme hook needed for the common case. If you're already happy with your styling stack, this isn't a draw — but if you want batteries-included, it's there.

5. Elements as plain values

Because every node is a value returned from a function, you can store, map, transform, and compose them with normal JavaScript — no special wrapper APIs. React hooks already let you share behaviour across components; MeoNode adds the same flexibility for structure:

const buttons = labels.map(label =>
  Button(label, { onClick: () => select(label) })
)

Row({ gap: 8, children: buttons })

This isn't impossible in JSX, but reaching for React.Children.map, fragments, and array keys often gets in the way.

Genuine performance differentiators

These are MeoNode-specific and don't come from "skipping JSX":

  • Node-level memoization — pass a dependency array as the second argument (Div({ ... }, [dep])) to memoize an entire subtree without reaching for React.memo.
  • React Server Components — nodes are serializable and work with the Next.js App Router.

Both are real, measurable, and worth the page on their own.

FAQ

Is this harder to read?

Different, mostly. Without angle brackets, the file looks like a tree of function calls — which matches the actual shape of the UI. Most engineers adjust within a day; some end up preferring it, others don't.

Can I still use existing React libraries?

Yes. Wrap any third-party component with Node() and it behaves like a MeoNode element — props, hooks, refs, and event handlers all work unchanged. Material UI, Recharts, React Router, etc. all compose normally.

What about Hooks?

The Rules of Hooks still apply. Because MeoNode uses normal if and && instead of JSX's {cond && <X />}, it's easy to call a hook-using function conditionally by accident.

Solution: wrap hook-using components in Node() or an arrow function — condition && Node(MyComponent) or condition && (() => MyComponent()). Both restore a real component boundary.

Is it compatible with Next.js / Vite?

Yes — both work out of the box, including Next.js Server Components. There's no special configuration beyond standard React setup.

When NOT to use MeoNode

To be honest about the trade:

  • Your team is JSX-fluent and has no friction with it
  • You rely heavily on JSX-specific tooling (Storybook addons, design-to-code generators)
  • You're onboarding junior engineers who learned React from JSX-only resources
  • Your codebase is large and mid-flight on a different paradigm

In any of those cases, the migration cost will outweigh the ergonomic gains. MeoNode shines most in new projects, in TypeScript-heavy codebases, and for teams that already lean functional.

On this page