This guide assumes that you have basic JavaScript knowledge. If not, MDN is a good place to start.
Introduction
To develop a rvx application locally, you need a recent version of NodeJS or any other compatible JavaScript runtime.
You can use the commands below to setup a minimal rvx project using Vite or Webpack and TypeScript:
# Create a "my-app" directory from rvx's "vite-ts" template project:
npx degit mxjp/rvx/templates/vite-ts my-app
# Move into "my-app":
cd my-app
# Install dependencies:
npm install
# Start a development server:
npm start
# Create a "my-app" directory from rvx's "webpack-ts" template project:
npx degit mxjp/rvx/templates/webpack-ts my-app
# Move into "my-app":
cd my-app
# Install dependencies:
npm install
# Start a development server:
npm start
Entry Point
After setting up the quick start template, you can find the main entry point in src/main.tsx
:
import { mount } from "rvx";
mount(
document.body,
<h1>Hello World!</h1>
);
<h1>Hello World!</h1>
expression directly creates an element and the mount
function takes whatever content is supported by rvx and appends it to the document body.
State & Reactivity
Reactivity is entirely based on signals which are objects that hold an arbitrary value:
import { sig } from "rvx";
// Create a signal with the initial value 0:
const count = sig(0);
When a signal is used directly or it's value is accessed through a function call, the signal can notify it's observers when the value changes:
import { mount, sig } from "rvx";
const count = sig(0);
mount(
document.body,
<>
{/* Using the signal directly is reactive: */}
Current count: {count}
{/* Accessing it's value through a function is reactive: */}
Is even count: {() => (count.value & 1) === 0}
{/*
Using the value directly is not reactive because
rvx has no way of re-evaluating the expression:
*/}
Initial count: {count.value}
</>
);
To replace a signal value, you can set the value
property:
count.value = 1;
To update an object, you can use the update
function.
const values = sig([7, 42]);
// This will modify the inner value and then notify observers:
values.update(values => {
values.push(77);
});
// Note, that deeply modifying objects directly does nothing:
values.value.push(77);
Basic Rendering
JSX expressions directly create HTML elements:
<button>Click me!</button>
By default, attribute names are the same as in HTML:
<img alt="An image" src="..." />
Attributes, set to false
, undefined
or null
are removed. true
is set as an empty string:
<input disabled={false} /> // <input>
<input disabled={true} /> // <input disabled="">
Attributes prefixed with prop:
are set using JavaScript properties:
<input type="text" prop:value="Hello World!" />
Event Listeners
Attributes prefixed with on:
are added as event listeners.
<button on:click={event => {
console.log("Clicked", event.target);
}}>Click me!</button>
Conditional Rendering
To render conditional or repeated content rvx uses so called Views which are sequences of DOM nodes that can change over time.
The Show
component renders content when a condition is met:
import { mount, sig, Show } from "rvx";
const showMessage = sig(false);
mount(
document.body,
<>
<button on:click={() => { showMessage.value = !showMessage.value }}>
Toggle message
</button>
<Show when={showMessage}>
{() => <h1>Hello World!</h1>}
</Show>
</>
);
The For
component repeats content for each unique item in an iterable:
import { mount, sig, For } from "rvx";
const values = sig<number[]>([]);
mount(
document.body,
<>
<button on:click={() => { values.update(v => v.push(Date.now())) }}>
Add current time
</button>
<ul>
<For each={values}>
{value => <li>{value}</li>}
</For>
</ul>
</>
);
In addition to Show
and For
, rvx provides some more view types you can find here or you can implement your own views for special use cases.
Components
Components in rvx are just functions that return arbitrary content. They are called once when the component is rendered.
function Message() {
return <h1>Hello World!</h1>;
}
// Using the component:
<Message />;
Properties are passed as is via the props
argument. Properties are static by default.
function Message(props: { message: string; }) {
return <h1>{props.message}</h1>;
}
// Using the component:
<Message message="Hello World!" />;
To make properties reactive, you can use the Expression
type which can be a static value, a signal or a function:
import { Expression } from "rvx";
function Message(props: { message: Expression<string>; }) {
return <h1>{props.message}</h1>;
}
// Using the component:
<Message message="Hello World!" />;
<Message message={() => "Hello World!"} />;
<Message message={someSignal} />;
To compute something from an expression value or evaluate it, you can use the map
and get
functions:
import { Expression, map, get } from "rvx";
function Message(props: { message: Expression<string>; }) {
console.log("Initial message:", get(props.message));
return <h1>{map(props.message, m => m.toUpperCase())}</h1>;
}
To allow components to update a value, you can use the Signal
type:
import { mount, sig, Signal } from "rvx";
function TextInput(props: { value: Signal<string>; }) {
return <input
type="text"
prop:value={props.value}
on:input={event => {
props.value.value = (event.target as HTMLInputElement).value;
}}
/>;
}
const text = sig("Hello World!");
<TextInput value={text} />;
Component children are passed via the children
property:
function Message(props: { children?: unknown; }) {
return <h1>{props.children}</h1>;
}
<Message>Hello World!</Message>;
From here, you can take a look at the reference to get an overview of what's possible.