Getting Started
If you are new to rEFui, this is the perfect place to begin. The Getting Started guide provides a complete walkthrough, from initial setup to building your first reactive components.
Project Setup
To get started, you'll need a project with a modern build tool like Vite, Rollup, or Webpack that can transpile JSX.
Configuring JSX
rEFui supports both JSX automatic and classic transformation methods. Prefer Classic Transform for the best flexibility and Automatic runtime for the ease of use.
Bun
In your tsconfig.json, configure compilerOptions:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "refui"
}
}
Vite (vite.config.js)
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
esbuild: {
jsx: 'automatic',
jsxImportSource: 'refui'
},
});
Babel (.babelrc.json)
{
"presets": [
[
"@babel/preset-react",
{
"runtime": "automatic",
"importSource": "refui"
}
]
]
}
For more details on JSX configuration, see the JSX Setup Guide.
Your First Component
Let's create and render a "Hello, World!" component to the DOM.
"Hello, World!"
index.html:
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script type="module" src="index.jsx"></script>
</body>
</html>
index.jsx:
import { createDOMRenderer } from 'refui/dom';
import { defaults } from 'refui/browser';
// 1. Create a DOM renderer instance
const renderer = createDOMRenderer(defaults);
// 2. Define your component
const App = () => {
return <h1>Hello, World!</h1>;
};
// 3. Render the component to a DOM element
renderer.render(
document.getElementById('app'),
App
);
Building a Reactive Counter
Now, let's introduce state with signals to create an interactive counter.
index.jsx:
import { createDOMRenderer } from 'refui/dom';
import { defaults } from 'refui/browser';
import { signal } from 'refui';
// 1. Create renderer
const renderer = createDOMRenderer(defaults);
// 2. Define the Counter component
const Counter = () => {
// Create a reactive signal with an initial value of 0
const count = signal(0);
// The component's UI will automatically update when `count` changes
return (
<div>
<h1>Count: {count}</h1>
<button on:click={() => count.value++}>
Increment
</button>
<button on:click={() => count.value--}>
Decrement
</button>
</div>
);
};
// 3. Render the component
renderer.render(
document.getElementById('app'),
Counter
);
Working with Lists & Conditionals
rEFui provides built-in components to handle common UI patterns like conditional rendering and loops.
Conditional Rendering with If
The <If> component renders content based on a condition.
import { signal, If, $ } from 'refui';
const LoginStatus = () => {
const isLoggedIn = signal(false);
return (
<div>
<If condition={isLoggedIn}>
{() => <p>Welcome back!</p>}
{() => <p>Please log in.</p>}
</If>
<button on:click={() => isLoggedIn.value = !isLoggedIn.value}>
{$(() => (isLoggedIn.value ? 'Logout' : 'Login'))}
</button>
</div>
);
};
Rendering Lists with For
The <For> component efficiently renders and updates lists of items. You can provide a function as a child that receives the item and returns a renderable node.
For a more dynamic example, checkout For.
import { signal, For } from 'refui';
const TodoList = () => {
const todos = signal([
{ text: 'Learn rEFui' },
{ text: 'Build an app' },
{ text: 'Profit' },
]);
return (
<ul>
<For entries={todos}>
{({ item }) => <li>{item.text}</li>}
</For>
</ul>
);
};
Handling Asynchronous Operations with Async
The <Async> component simplifies handling promises. You provide it a future (a promise) and children to render for the pending, resolved, and error states.
import { Async } from 'refui';
// A mock API call that resolves after 1 second
const fetchUser = () => new Promise((resolve) => {
setTimeout(() => resolve({ name: 'John Doe' }), 1000);
});
const UserProfile = () => {
const userPromise = fetchUser();
return (
<Async
future={userPromise}
fallback={() => <p>Loading user...</p>}
catch={({ error }) => <p>Error: {error.message}</p>}
>
{({ result: user }) => <p>Welcome, {user.name}!</p>}
</Async>
);
};
A Note on Reactivity in JSX
rEFui is a retained mode renderer. This means JSX templates are evaluated once to create and render the initial UI structure. They are not functions that get re-executed automatically.
Because of this, you cannot place dynamic expressions directly in your JSX and expect them to be reactive.
// ❌ This will NOT update when `count` changes
const IncorrectCounter = () => {
const count = signal(0);
return (
<div>
{/* This expression is evaluated only once! */}
<p>Count is: {count.value}</p>
<button on:click={() => count.value++}>Increment</button>
</div>
);
};
To make expressions reactive, you must wrap them in a computed signal using $(...) (an alias for computed). This creates a new signal that rEFui can track for updates.
// ✅ This will update correctly
import { signal, $ } from 'refui';
const CorrectCounter = () => {
const count = signal(0);
// Create a computed signal for the text
const message = $(() => `Count is: ${count.value}`);
return (
<div>
{/* Use the computed signal here */}
<p>{message}</p>
<button on:click={() => count.value++}>Increment</button>
</div>
);
};
Key Takeaway: If you have an expression in JSX that depends on a signal, wrap it in $(...) to ensure it updates when the signal's value changes. Simple signal references, like {count}, are automatically handled by the renderer.
Inline helper functions follow the same rule. When a function appears inline as a child or prop value, rEFui evaluates it immediately—recursively invoking any returned functions until it resolves to a concrete node. These calls run during render only and do not subscribe to signals, so prefer explicit signals or computed wrappers for reactive behavior.
Next Steps
You've now learned the basics of rEFui! To continue your journey, explore the detailed documentation:
- Signals: Master the reactive system.
- Components: Discover all built-in components.
- DOM Renderer: Learn about DOM-specific features.
- HTML Renderer: Get started with server-side rendering.
- JSX Setup: Advanced JSX configurations.
- Core API: Explore the full rEFui API.