Top Related Projects
The library for web and native user interfaces.
⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
:fire: An extremely fast, React-like JavaScript library for building modern user interfaces
Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
web development for the rest of us
This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
Quick Overview
JSX Control Statements is a Babel plugin that extends JSX syntax to include control statements like conditionals and loops. It allows developers to write more expressive and readable JSX code by providing familiar control flow constructs directly within JSX, without resorting to JavaScript expressions or separate logic.
Pros
- Improves readability and maintainability of JSX code
- Reduces the need for complex JavaScript expressions within JSX
- Provides a more natural and intuitive way to handle conditional rendering and loops
- Seamlessly integrates with existing React projects
Cons
- Requires an additional build step (Babel plugin)
- Deviates from standard JSX syntax, which may confuse developers unfamiliar with the library
- Limited adoption compared to standard JSX practices
- May lead to overuse of logic in the view layer, potentially violating separation of concerns
Code Examples
- Conditional Rendering:
<If condition={isLoggedIn}>
<span>Welcome, {username}!</span>
<Else />
<span>Please log in</span>
</If>
- Looping:
<For each="item" of={items}>
<li key={item.id}>{item.name}</li>
</For>
- Switch Statement:
<Switch>
<Case condition={status === 'loading'}>
<LoadingSpinner />
</Case>
<Case condition={status === 'error'}>
<ErrorMessage />
</Case>
<Default>
<Content />
</Default>
</Switch>
Getting Started
- Install the package:
npm install --save-dev jsx-control-statements
- Add the plugin to your Babel configuration (e.g., in
.babelrc
):
{
"plugins": ["jsx-control-statements"]
}
- Import the components in your React file:
import { If, For, Choose, When, Otherwise } from 'jsx-control-statements';
- Start using JSX Control Statements in your React components:
function MyComponent({ items, isLoading }) {
return (
<div>
<If condition={isLoading}>
<LoadingSpinner />
<Else />
<For each="item" of={items}>
<Item key={item.id} {...item} />
</For>
</If>
</div>
);
}
Competitor Comparisons
The library for web and native user interfaces.
Pros of React
- Extensive ecosystem and community support
- Robust performance optimizations and virtual DOM
- Comprehensive documentation and learning resources
Cons of React
- Steeper learning curve for beginners
- Requires additional libraries for complex state management
- More verbose syntax for conditional rendering
Code Comparison
React:
{isLoggedIn ? (
<LogoutButton />
) : (
<LoginButton />
)}
jsx-control-statements:
<If condition={isLoggedIn}>
<LogoutButton />
<Else />
<LoginButton />
</If>
Key Differences
- jsx-control-statements provides a more declarative syntax for control flow
- React's approach is more JavaScript-like, while jsx-control-statements offers a JSX-centric solution
- jsx-control-statements requires additional setup and tooling
Use Cases
- React: Large-scale applications, complex UIs, and projects requiring extensive ecosystem support
- jsx-control-statements: Projects prioritizing readability and declarative JSX syntax for control flow
Community and Maintenance
- React: Actively maintained by Facebook and a large community
- jsx-control-statements: Smaller community, less frequent updates
Integration
- React: Core library for building user interfaces
- jsx-control-statements: Can be used alongside React to enhance JSX capabilities
⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
Pros of Preact
- Lightweight alternative to React with a smaller bundle size
- Faster performance due to its minimal core and efficient rendering
- Compatible with most React libraries and ecosystem
Cons of Preact
- Smaller community and ecosystem compared to React
- Some advanced React features may not be available or require additional configuration
- Learning curve for developers transitioning from React
Code Comparison
jsx-control-statements:
<If condition={isTrue}>
<span>Rendered when true</span>
</If>
<For each="item" of={items}>
<div>{item}</div>
</For>
Preact:
{isTrue && <span>Rendered when true</span>}
{items.map(item => (
<div>{item}</div>
))}
Summary
Preact is a lightweight alternative to React, offering improved performance and smaller bundle sizes. It maintains compatibility with most React libraries while providing a similar development experience. However, it has a smaller community and may lack some advanced React features.
jsx-control-statements, on the other hand, focuses on providing additional JSX control structures for React applications. It offers a more declarative syntax for conditional rendering and loops, which some developers may find more readable.
The choice between these libraries depends on project requirements, performance needs, and developer preferences. Preact is better suited for projects prioritizing performance and size, while jsx-control-statements can enhance the readability of React code in certain scenarios.
:fire: An extremely fast, React-like JavaScript library for building modern user interfaces
Pros of Inferno
- Higher performance and smaller bundle size compared to React
- Supports isomorphic rendering for server-side and client-side applications
- Offers a more flexible component model with lifecycle events
Cons of Inferno
- Smaller community and ecosystem compared to React-based solutions
- Learning curve for developers familiar with React, as there are some API differences
- Limited third-party library support compared to React ecosystem
Code Comparison
jsx-control-statements:
<If condition={test}>
<span>Test passed</span>
</If>
<For each="item" of={items}>
<span>{item}</span>
</For>
Inferno:
{test && <span>Test passed</span>}
{items.map(item => <span>{item}</span>)}
Summary
Inferno is a high-performance JavaScript library for building user interfaces, while jsx-control-statements is a Babel plugin that adds additional control statements to JSX. Inferno offers better performance and more flexibility, but has a smaller ecosystem. jsx-control-statements provides a more declarative syntax for control flow in JSX, but is limited to React-based projects. The choice between them depends on project requirements, performance needs, and developer preferences.
Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
Pros of htm
- No build step required, works directly in browsers
- Lightweight and fast, with a small bundle size
- Supports template literals for dynamic content
Cons of htm
- Less familiar syntax compared to JSX
- Limited ecosystem and community support
- May require additional setup for TypeScript integration
Code Comparison
jsx-control-statements:
<If condition={test}>
<span>Test passed</span>
</If>
<For each="item" of={items}>
<span>{item}</span>
</For>
htm:
html`
${test && html`<span>Test passed</span>`}
${items.map(item => html`<span>${item}</span>`)}
`
Summary
jsx-control-statements provides a more familiar JSX-like syntax for control flow, while htm offers a lightweight alternative using template literals. jsx-control-statements requires a build step but integrates well with existing React ecosystems. htm is more flexible and can be used without compilation, but may have a steeper learning curve for developers accustomed to JSX. Both libraries aim to simplify conditional rendering and list iteration in component-based UI development.
web development for the rest of us
Pros of Svelte
- Full-featured framework for building web applications, not just a JSX extension
- Compiles to highly efficient vanilla JavaScript, resulting in smaller bundle sizes
- Reactive by design, with built-in state management and component lifecycle
Cons of Svelte
- Steeper learning curve due to its unique syntax and compilation process
- Smaller ecosystem compared to React and its extensions
- Less flexibility in terms of integrating with existing React projects
Code Comparison
jsx-control-statements:
<If condition={x > 0}>
<span>x is positive</span>
</If>
<For each="item" of={items}>
<li>{item}</li>
</For>
Svelte:
{#if x > 0}
<span>x is positive</span>
{/if}
{#each items as item}
<li>{item}</li>
{/each}
While jsx-control-statements adds control flow to JSX, Svelte provides similar functionality as part of its core syntax. Svelte's approach is more integrated into the framework, while jsx-control-statements is an extension for React projects. Svelte offers a more comprehensive solution for building web applications, but jsx-control-statements may be easier to adopt for existing React codebases.
This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
Pros of Vue
- Comprehensive framework with built-in state management, routing, and CLI tools
- Larger community and ecosystem, with more resources and third-party libraries
- Progressive framework allowing gradual adoption in existing projects
Cons of Vue
- Steeper learning curve for developers new to component-based frameworks
- Potentially more complex setup for small projects or simple use cases
- Larger bundle size compared to lightweight JSX control statements
Code Comparison
Vue template syntax:
<template>
<div v-if="condition">
<p>{{ message }}</p>
</div>
</template>
jsx-control-statements syntax:
<If condition={condition}>
<p>{message}</p>
</If>
Summary
Vue is a full-featured framework offering a complete solution for building web applications, while jsx-control-statements is a lightweight library focused on enhancing JSX with control flow statements. Vue provides more out-of-the-box functionality and community support, but may be overkill for simpler projects. jsx-control-statements offers a more focused approach, integrating seamlessly with existing React or JSX-based projects, but lacks the comprehensive tooling and ecosystem of Vue.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
JSX Control Statements
JSX-Control-Statements is a Babel plugin that extends JSX to add basic control statements: conditionals and loops.
It does so by transforming component-like control statements to their JavaScript counterparts - e.g. <If condition={condition()}>Hello World!</If>
becomes condition() ? 'Hello World!' : null
.
Developers coming to React from using JavaScript templating libraries like Handlebars are often surprised that there's no built-in looping or conditional syntax. This is by design - JSX is not a templating library, it's declarative syntactic sugar over functional JavaScript expressions. JSX Control Statements follows the same principle - it provides a component-like syntax that keeps your render
functions neat and readable, but desugars into clean, readable JavaScript.
The only dependency JSX-Control-Statements relies upon is Babel. It is compatible with React and React Native.
:skull_and_crossbones: Beware: This is a Babel plugin. It changes your code to other code - this means that some tooling that looks at your code (e.g. static analysis, typescript) is likely to not work. This plugin dates back to when JSX was daring and Javascript was more like playdough than meccano - if you want to stay on the well-trodden path stick with writing &&
and map
.
Table of Contents
- A Note on Transformation and Alternative Solutions
- Installation
- Syntax
- Linting
- Alternative Solutions
- What about Typescript
- Major Versions
- I Want to Contribute!
A Note on Transformation and Alternative Solutions
It appears to be pretty easy to implement conditionals as React component, which is underlined by the amount of libraries which have taken this approach. However, all of them suffer from the same major caveat: A React component will always evaluate all of its properties including the component body. Hence the following example will fail for those libraries:
<IfComponent condition={item}>{item.title}</IfComponent>
The error will be "Cannot read property 'title' of undefined", because React will evaluate the body of the custom component and pass it as "children" property to it. The only workaround is to force React into lazy evaluation by wrapping the statement in a function.
This is the reason why conditionals must be implemented in pure JS. JSX-Control-Statements only adds the syntactic sugar to write conditionals as component, while it transforms this "component" to a pure JS expression.
See Alternative Solutions for a more detailed comparison and pure JS solutions.
Installation
As a prerequisite you need to have Babel installed and configured in your project.
Install via npm:
npm install --save-dev babel-plugin-jsx-control-statements
Then you only need to specify JSX-Control-Statements as Babel plugin, which you would typically do in your .babelrc
.
{
...
"plugins": ["jsx-control-statements"]
}
If you use the transform-react-inline-elements
plugin, place it after jsx-control-statements
:
{
...
"plugins": ["jsx-control-statements", "transform-react-inline-elements"]
}
Babel can be used and configured in many different ways, so use this guide to pick a configuration which fits your setup.
Syntax
If Tag
Used to express the most simple conditional logic.
// simple
<If condition={ true }>
<span>IfBlock</span>
</If>
// using multiple child elements and / or expressions
<If condition={ true }>
one
{ "two" }
<span>three</span>
<span>four</span>
</If>
<If>
The body of the if statement only gets evaluated if condition
is true.
Prop Name | Prop Type | Required |
---|---|---|
condition | boolean | :white_check_mark: |
<Else /> (deprecated)
The else element has no properties and demarcates the else
branch.
This element is deprecated, since it's bad JSX/XML semantics and breaks auto-formatting.
Please use <Choose>
instead.
Transformation
If statements transform to the ternary operator:
// before transformation
<If condition={test}>
<span>Truth</span>
</If>;
// after transformation
{
test ? <span>Truth</span> : null;
}
Choose Tag
This is an alternative syntax for more complex conditional statements. Its a equivalent of switch
statement for jsx. The syntax itself is XMLish and conforms by and
large to JSTL or XSLT (the attribute is called condition
instead of test
):
<Choose>
<When condition={ test1 }>
<span>IfBlock</span>
</When>
<When condition={ test2 }>
<span>ElseIfBlock</span>
<span>Another ElseIfBlock</span>
<span>...</span>
</When>
<Otherwise>
<span>ElseBlock</span>
</Otherwise>
</Choose>
// default block is optional; minimal example:
<Choose>
<When condition={true}>
<span>IfBlock</span>
</When>
</Choose>
<Choose>
Acts as a simple container and only allows for <When>
and <Otherwise>
as children.
Each <Choose>
statement requires at least one <When>
block but may contain as many as desired.
The <Otherwise>
block is optional.
<When>
Analog to <If>
.
Prop Name | Prop Type | Required |
---|---|---|
condition | boolean | :white_check_mark: |
<Otherwise>
<Otherwise>
has no attributes and demarcates the else branch of the conditional.
Transformation
This syntax desugars into a (sequence of) ternary operator(s).
// Before transformation
<Choose>
<When condition={test1}>
<span>IfBlock1</span>
</When>
<When condition={test2}>
<span>IfBlock2</span>
</When>
<Otherwise>
<span>ElseBlock</span>
</Otherwise>
</Choose>;
// After transformation
{
test1 ? (
<span>IfBlock1</span>
) : test2 ? (
<span>IfBlock2</span>
) : (
<span>ElseBlock</span>
);
}
For Tag
Define <For>
like so:
// you must provide the key attribute yourself
<For each="item" of={ this.props.items }>
<span key={ item.id }>{ item.title }</span>
</For>
// using the index as key attribute is not stable if the array changes
<For each="item" index="idx" of={ [1,2,3] }>
<span key={ idx }>{ item }</span>
<span key={ idx + '_2' }>Static Text</span>
</For>
Prop Name | Prop Type | Required | description |
---|---|---|---|
of | array or collection(Immutable) | :white_check_mark: | the array to iterate over. This can also be a collection (Immutable.js) or anything on which a function with the name map can be called |
each | string | a reference to the current item of the array which can be used within the body as variable | |
index | string | a reference to the index of the current item which can be used within the body as variable |
Note that a <For>
cannot be at the root of a render()
function in a React component, because then you'd
potentially have multiple components without a parent to group them which isn't allowed. As with <If>
, the same rules
as using Array.map()
apply - each element inside the loop should have a key
attribute that uniquely identifies it.
For Tag - Alternative Syntax
For those using Typescript, the previous syntax introduces several issues with undefined variables. To deal with this issue, we introduce a following syntax, inspired by tsx-control-statements.
// before transformation
<For
of={items}
body={(item, index) => (
<span key={item.id}>
{index}. {item.title}
</span>
)}
/>;
// after transformation
{
items.map(function(item, index) {
<span key={item.id}>
{index}. {item.title}
</span>;
});
}
Prop Name | Prop Type | Required | description |
---|---|---|---|
of | array or collection(Immutable) | :white_check_mark: | the array to iterate over. This can also be a collection (Immutable.js) or anything on which a function with the name map can be called |
body | map expression | expression of the map statement |
With Tag
Used to assign values to local variables:
// simple
<With foo={ 47 } bar={ 'test' }>
<span>{ foo }</span>
<span>{ bar }</span>
</With>
// nested
<With foo={ 47 }>
<With bar={ 'test' }>
<span>{ foo }</span>
<span>{ bar }</span>
</With>
</With>
Prop Name | Prop Type | Required | description |
---|---|---|---|
any name | any type | assign prop value to a local variable named by prop name |
You may assign multiple variables with a single <With>
statement. The defined variable is
available only within the <With>
block.
Transformation
<With>
statements transform to immediately-invoked function expressions:
// before transformation
<With foo={47}>
<span>{foo}</span>
</With>;
// after transformation
{
(function(foo) {
return <span>{foo}</span>;
}.call(this, 47));
}
Linting
ESLint
Since all control statements are transformed via Babel, no require
or import
calls are needed. This in turn
(well, and some more cases) would lead to warnings or errors by ESLint about undefined variables.
But fortunately you can use this ESLint plugin for JSX-Control-Statements to lint your code.
FlowType
There's still not a perfect solution for FlowType given that it doesn't provide a lot of plugin functionality
(at least not yet). Flow definitions are available in jsx-control-statements.latest.flow.js
for Flow >= 0.53, or jsx-control-statements.flow.js
(deprecated) for Flow < 0.53 - you can pick which file to use like this. These will stop the
type checker complaining about statements being undeclared. As of now there's no neat way to make the Flow checker
recognise each
attributes in <For>
loops as a variable - the best workaround for now is something like:
render() {
declare var eachVariable: string;
return (
<For each="eachVariable" of={["hello", "world"]}>
{eachVariable}
</For>
);
}
If you know of a better way to work around this please let us know!
Alternative Solutions
Pure JavaScript
Since everything will be compiled to JavaScript anyway, you might prefer to stick to pure JavaScript solutions.
Conditionals
Probably the most common way for simple conditionals is the use of the && operator:
// simple if
{
test && <span>true</span>;
}
// additionally the else branch
{
!test && <span>false</span>;
}
The ternary operator is probably more elegant for if / else conditionals:
// simple
{
test ? <span>true</span> : <span>false</span>;
}
// with multiple children
{
test ? (
[<span key="1">one</span>, <span key="2">two</span>]
) : (
<span>false</span>
);
}
Another approach is to refactor your conditional into a function:
testFunc(condition){
if(condition) {
return <span>true</span>;
}
else {
return <span>false</span>
}
}
render() {
return (
<div>{ testFunc(test) }</div>
)
}
Loops
Not many options here:
{
items.map(function(item) {
<span key={item.id}>{item.title}</span>;
});
}
Comparison
Arguments pro JSX-Control-Statements in comparison to pure JS solutions:
- More intuitive and easier to handle for designers and people with non-heavy JS background
- JSX does not get fragmented by JS statements
- Better readability and neatness, but that probably depends on you
Cons:
- Penalty on build-time performance
- Depends on Babel 6
- Some Babel configuration
React Components
There are a reasonable amount of React components for conditionals (e.g. react-if, which inspired this in the first place), JSX-Control-Statements is the only approach we know of that avoids execution of all branches (see the intro section), and there seems to be no other component-based solution to looping - while it would be possible to make a component that renders everything in props.children
for every element of an array, you'd have to access the members of the array in that component instead of the one that uses it.
For more discussion on If
in React by the react team, have a look at https://github.com/reactjs/react-future/issues/35.
To sum up:
- Conditionals don't execute invalid paths
- Loops with variable references to each element and index are made possible
- No penalty on runtime performance
- No import / require statements needed to use control statements
- It works exactly as JSX is supposed to work: Plain syntactic sugar
Cons:
- Depends on Babel 6
- Some Babel configuration
- Slightly longer build times
- Requires an extra plugin to work with ESLint
For macros
If you are looking to use macros with it, take a look at jsx-control-statements-macros which is basically the extension of jsx-control-statements with macros support.
What about Typescript?
There's a version for that by @KonstantinSimeonov!
Major Versions
- 4.x.x is a pure Babel plugin supporting Babel >= 7.
- 3.x.x is a pure Babel plugin supporting Babel >= 6.
- 2.x.x was a Babel plugin supporting Babel >= 6, and a set of JSTransform visitors.
- 1.x.x was a Babel plugin supporting Babel <= 5, and a set of JSTransform visitors.
This used to support both JSTransform and Babel, but as JSTransform is no longer maintained support was dropped. You can find the code for the JSTransform version at https://github.com/AlexGilleran/jsx-control-statements-jstransform.
I Want to Contribute!
Yay! Please read the Contributor's Guide.
Top Related Projects
The library for web and native user interfaces.
⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
:fire: An extremely fast, React-like JavaScript library for building modern user interfaces
Hyperscript Tagged Markup: JSX alternative using standard tagged templates, with compiler support.
web development for the rest of us
This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot