Why Build React without JSX?
React without JSX is not just a stylistic choice—it's a paradigm shift that unlocks better performance, simpler tooling, and superior type safety. MeoNode UI is built on the belief that components are just functions, and they should be treated that way.
The Problem with JSX
JSX is a syntax extension that looks like HTML but behaves like JavaScript. While popular, it introduces several layers of accidental complexity:
- Build Step Dependency: JSX must be transpiled. You cannot run JSX directly in the browser. This ties your project to heavy build tools like Babel, SWC, or TypeScript transformers.
- Indirection in Type Inference: While modern TypeScript (5.5+) has significantly improved JSX type checking,
function calls remain the most direct path for type inference. When you call
Component({ prop: value }), TypeScript immediately validates the function signature. With JSX, TypeScript must first resolve the JSX namespace, element types, and intrinsic elements before performing prop validation—adding cognitive and compilation overhead. - Divided Syntax: JSX forces you to constantly switch contexts between an XML-like syntax for markup and standard
JavaScript for logic (using
{}). This syntactic barrier breaks the natural flow of code and treats UI layout as something separate from the rest of your application logic.
The MeoNode Solution: Pure Composition
MeoNode UI removes the need for JSX by providing a lightweight, type-safe API for creating elements.
1. No Build Step Required
Because MeoNode components are just standard JavaScript functions, they run natively in any modern browser.
// Traditional React (Requires Build Step) const App = () => <div className="p-4">Hello</div>
// MeoNode UI (Runs Anywhere) const App = () => Node('div', { className: 'p-4', children: 'Hello' })
2. Superior Type Safety
When you call a function, TypeScript knows exactly what arguments it expects. There's no "magic string" matching or JSX namespace lookups.
- Strict Prop Validation: If a prop is missing or the wrong type, you get an immediate error.
- Generic Inference: Passing generics to components is natural:
Node<MyProps, typeof Component>(Component, props)vs the awkward<Component<MyProps> ... />syntax in JSX.
3. Unified Syntax
MeoNode treats UI elements as standard JavaScript values. You don't need a separate syntax for markup and logic.
JSX Way (Context Switching):
<div className="dashboard"> {/* Enter JS mode with curly braces */} {isLoggedIn ? ( isAdmin ? <AdminDashboard /> : <UserDashboard /> ) : ( <LoginForm /> )} </div>
MeoNode Way (Just JavaScript):
import { Div, Node } from '@meonode/ui' Div({ className: 'dashboard', children: isLoggedIn ? (isAdmin ? Node(AdminDashboard) : Node(UserDashboard)) : Node(LoginForm), }).render()
Both examples use the same logic. But in JSX, you constantly switch contexts: < for elements, { for expressions.
In MeoNode, you never leave JavaScript. Elements are functions, props are objects, children are arguments. The ternary isn't a "JSX feature"—it's just standard JavaScript. One syntax, one mental model.
4. Unified Styling & Theming
In the JSX world, you often need separate libraries for styling (styled-components, emotion) and separate providers
for theming. MeoNode builds this directly into the function call.
- Direct CSS Props: Every node accepts CSS properties as props (
padding,color), powered by @emotion/react under the hood. - Context-Based Theming: Theme tokens resolve automatically. No
useThemehook boilerplate needed for basic styling.
// JSX + Styled Components const StyledDiv = styled.div` color: ${props => props.theme.primary}; padding: 20px; ` // <StyledDiv>Hello</StyledDiv>
// MeoNode Div({ color: 'theme.primary', // Auto-resolved from Context padding: 20, children: 'Hello' })
5. True Composability
Functions compose better than components. In JSX, sharing logic often requires High Order Components (HOCs) or complex render props, which add nesting to the DOM and the code.
With MeoNode, a "component" is just a function that returns a Node. You can compose them using standard functional programming techniques—currying, partial application, and composition—without adding React runtime overhead.
Performance Benefits
MeoNode UI is optimized for "Direct to Node" memoization and modern React features.
- Zero Compile Time: Faster development server starts and hot reloads since there's no JSX transform.
- React Server Components (RSC): MeoNode nodes are fully serializable and work seamlessly in Next.js App Router.
- Surgical Memoization: Control re-renders with node-level dependency arrays
Div({ ... }, [dep]), avoiding the need forReact.memowrappers in many cases.
FAQ
Is this harder to read?
It might look different at first, but it's often more structured. By removing the visual noise of angle brackets
</>, your code looks like a clean tree of function calls, matching the actual structure of the UI tree.
Can I still use existing React libraries?
Yes. MeoNode UI is fully compatible with the React ecosystem. You can use the Node() helper to render any
third-party React component (like Material UI, Recharts, or React Router).
What about Hooks and Conditional Rendering?
You must still follow the Rules of Hooks. Since we use native
if statements, it's easy to accidentally call a hook-using component conditionally.
Solution: Always wrap hook-using components in Node() or an inline arrow function:
condition && Node(MyComponent) or condition && (() => MyComponent()).
Is it compatible with Next.js / Vite?
Absolutely. MeoNode UI works seamlessly with Next.js App Router (Server Components) and Vite. Setup is often simpler as you have fewer configuration files to manage.
On this page
- Why Build React without JSX?