Introduction
🎉🎊 Thank you for checking out Allotize. If you have any question, don't hesitate to ask a question via GitHub Issues.
What is Allotize?
Allotize provides a platform for building modern web applications without backend code, servers or even databases. Yet, you are able to build apps such as collaborative planning tools or social media platforms.
Think of Allotize as something that gives your data superpowers, and allows it to teleport and sync between devices!
How it works
You connect variables, objects, or any other JavaScript code to Allotize. Your users will then be able to detect updates and changes on each others' machines.
This means that you can build message boards, collaborative services, games or anything you could think of with just client-side code. Here is an example of a cube that can be rotated by users along with an upvoting system.

Let's take a look at the upvoting code.
We simply create an object votes
that holds our information and register it to Allotize via Allotize.Crate(votes)
.
const votes = Allotize.Crate({
route: "cube/votes",
data: {
upvotes: 0,
},
onChange: (oldData, newData) => {
document.getElementById("upvotes").innerHTML = newData.upvotes;
},
});
document.getElementById("upvote").addEventListener('click', (e) => {
votes.data.upvotes += 1;
});
document.getElementById("downvote").addEventListener('click', (e) => {
votes.data.upvotes -= 1;
});
That's it! Everyone who loads our website will have a synced number of upvotes that changes in real-time. Proceed to the next chapter, to find out how you can create your own app!
Tutorial: Creating a To-Do application
This tutorial will help you get up and running with Allotize.
You are not able to follow this tutorial yet
This tutorial is under construction
Assumptions:
- You have experience of JavaScript
- You have some experience with React
In this tutorial, you learn:
- How to create a simple collaborative To-Do app with Allotize
- A workflow for developing applications using Allotize
- Common pitfalls when using Allotize and how to avoid them
- How to publish your app to GitHub Pages
The App
We are building a simple To-Do App, but it's not any To-Do App, this one will have collaboration support and sync its state between users in real-time!
Here is an example of the app we are building. To the left is a view of the app running in Firefox, to the right you can see the same app in Chrome.
App Requirements
- Users should be able create a new To-Do
- Users should be able to mark a To-Do as done
- Users should be able to remove a To-Do
- Users should be able to change the description of a To-Do
- All updates should be synced in real-time without conflicts
- Opening and closing the app should not clear the To-Dos
Setup
This section describes how to install Allotize and crate a new app.
Install using create-allotize-app
Allotize
is published to npm, along with a utility tool to
quickly start a new Allotize app called create-allotize-app
.
If you don't already have npm
installed, you can find
instructions on how to install it here.
To create your project, run this command:
npm init allotize-app my-allotize-app
This will download a project template running Allotize and React. If everything went as expected, you should receive a message:
Thank you for downloading the Allotize template
Proceed by navigating to your newly created project and install the dependencies:
cd my-allotize-app
npm install
Once installed, you can start the development server via:
npm run start
Go ahead and visit localhost:8080 to make sure everything is running smoothly.
✨ Great job at creating your first Allotize app! In the next section, we will be looking at how to create a new component using Allotize.
Create a To-Do component
For our To-Do application to work, we need to create a To-Do component that contains the information associated with a To-Do.
Create a new file src/js/components/Todo.jsx
.
The first thing we need to do is to create a new state of our To-Do.
Allotize uses a URI scheme to be able to identify different resources.
We will discover our To-Do's via todo/:todoId
and use a unique id for every entry.
To do so, we use the hook called useAllotizeState
available in the allotize-react
package.
In this case, we want to create a new route to point to the state of our To-Do.
Telling Allotize that this is our intention, we simply set route
to match
our access pattern and state
to some initial state.
import { useAllotizeState } from "allotize-react";
export function Todo(props) {
const [state, setState] = useAllotizeState({
route: `todo/${props.todoId}`,
state: {
description: props.desc,
done: false,
},
});
return (
<div>
</div>
);
}
Now we can use state
and setState
just like we use the regular useState-hook.
In our case, we want a button that can mark the state.done
as true/false
.
import React, { useEffect, useRef, useState } from "react";
import { useAllotizeState } from "allotize-react";
export function Todo(props) {
const [state, setState] = useAllotizeState({
route: `todo/${props.todoId}`,
state: {
description: props.desc,
done: false,
},
});
return (
<div>
[TODO-{props.todoId}]
<button onClick={() => setState({...state, done: !state.done})}>
{ state.done ? "Restore" : "Done"}
</button>
</div>
);
}
Finally, we add some extra styling and buttons to our To-Do. This part can be skipped if you don't want to style your components using bootstrap.
import React, { useEffect, useRef, useState } from "react";
import { useAllotizeState } from "allotize-react";
const todoStyle = {
padding: "12px",
}
const doneStyle = {
opacity: 0.4,
}
export function Todo(props) {
const [state, setState] = useAllotizeState({
route: `todo/${props.todoId}`,
state: {
description: props.desc,
done: false,
},
});
const [edit, setEdit] = useState(false);
const editInput = useRef();
useEffect(() => {editInput.current && editInput.current.focus();});
return (
<div style={state.done ? {...todoStyle, ...doneStyle} : todoStyle}>
<div className="card">
<div className="card-header">
<strong className="mr-auto">
{state.done ? "Done - " : ""}[TODO-{props.todoId}]
</strong>
<div className="btn-group btn-group-sm float-right" role="group" aria-label="Basic example">
<button className="btn btn-primary" onClick={() => setState({...state, done: !state.done})}>
{ state.done ? "Restore" : "Done"}
</button>
<button className="btn btn-secondary" onClick={() => setEdit(true)}>Edit</button>
<button className="btn btn-danger" onClick={() => props.deleteTodo(props.todoId)}>
Delete
</button>
</div>
</div>
<div className="card-body">
{
edit ?
<form className="input-group mb-3" onSubmit={() => setEdit(false)}>
<input
ref={editInput}
type="text"
className="form-control"
name="description"
onChange={(e) => setState({...state, description: e.target.value})}
placeholder="To-Do description"
value={state.description}
aria-describedby="button-addon2"/>
<div className="input-group-append">
<button className="btn btn-primary" type="submit" id="button-addon2">
Save
</button>
</div>
</form>
:
state.description
}
</div>
</div>
</div>
);
}
Creating a container for our To-Dos
Create a new file src/containers/TodoTracker.jsx
.
import React, { useState } from "react";
import { Todo } from './Todo.jsx'
import { useAllotizeState } from "allotize-react";
export function TodoTracker(props) {
const [state, setState] = useAllotizeState({
route: "todo-collection",
state: {
todos: [],
id: 0,
}
});
const [newTodoDesc, setNewTodoDesc] = useState("");
const handleTodoDescChange = (e) => setNewTodoDesc(e.target.value);
const createTodo = (e) => {
e.preventDefault();
setState({
todos: state.todos.concat({desc: newTodoDesc, id: state.id}),
id: state.id + 1,
});
setNewTodoDesc("");
};
const deleteTodo = (id) => {
setState({
...state,
todos: state.todos.filter(todo => todo.id !== id),
});
};
return (
<div>
<form className="input-group mb-3" onSubmit={createTodo}>
<input
type="text"
className="form-control"
name="description"
onChange={handleTodoDescChange}
placeholder="To-Do description"
value={newTodoDesc}
aria-describedby="button-addon2"/>
<div className="input-group-append">
<button className="btn btn-primary" type="submit" id="button-addon2">
Create
</button>
</div>
</form>
{state.todos.map(todo => {
return (
<Todo
key={todo.id}
desc={todo.desc}
todoId={todo.id}
deleteTodo={deleteTodo}
/>
);
})}
</div>
);
}
Allotize Concepts
Installation
Use Allotize
npm install allotize@0.0.1-alpha.1
Allotize is internally using WebAssembly (WASM), which must be dynamically imported.
Information on dynamic imports can be found at https://javascript.info/modules-dynamic-imports.
You will also need to handle .wasm
files.
If you struggle setting up WASM support, you can use our template create-allotize-app
(see bottom of the page).
Use Allotize with React
npm install react
npm install allotize@0.0.1-alpha.1
npm install allotize-react@0.0.1-alpha
Allotize is internally using WebAssembly, which must be dynamically imported.
Information on dynamic imports can be found at https://javascript.info/modules-dynamic-imports.
You will also need to handle .wasm
files.
If you struggle setting up WASM support, you can use our template create-allotize-app
(see bottom of the page).
Create an Allotize app with React using create-allotize-app
For the easiest setup, we recommend using create-allotize-app
, which will
automatically handle the dynamic import of allotize
. In addition, this
template includes helpers for
npm init allotize-app my-allotize-app
cd my-allotize-app
npm install
Crates
Crates is a resource that is available in the Allotize ecosystem. A crate aims to act like a box of information and you as a developer is able to define what data it includes, what is shared and what happens when it changes.
Let's take a quick look at a very simple example of a Crate.
If we want to create a Counter
that is available and editable by everyone on our website,
we can simply define the user at the route counters/<counter>
and define the desired data scheme.
const Counter = (counter) => Allotize.Crate({
route: "counters/" + counter,
state: {
count: 0,
},
onChange: (oldState, newState) => {
console.log(newState.count);
}
});
const counter = Counter("counter-1");
counter.state.count += 1;
Common Crate Patterns
Thera are some common crate patterns you can use:
State
const State = Allotize.Crate({
route: "myState",
state: {
count: 0,
},
onChange: (oldState, newState) => {
console.log(newState.count);
}
});
Stream
A stream crate will not persist any data, this means that you can use this crate as a simple message channel that holds the last transmitted message.
const Stream = Allotize.Crate({
route: "myStream",
persist: false,
state: {
count: 0,
},
onChange: (oldState, newState) => {
console.log(newState.count);
}
});
Generator
Oftentimes you want to be able to dynamically create new routes:
let counter = (name) => Allotize.Crate({
route: `counters/${name}`,
state: {
count: 0,
},
})
React.js
By using the allotize-react
package, you can use the useAllotizeState
-hook
to easily use Allotize in your app.
Installation
npm install react
npm install allotize@0.0.1-alpha.1
npm install allotize-react@0.0.1-alpha
Allotize is internally using WebAssembly, which must be dynamically imported.
Information on dynamic imports can be found at https://javascript.info/modules-dynamic-imports.
You will also need to handle .wasm
files.
If you struggle setting up WASM support, you can use our template create-allotize-app
(see bottom of the page).
Usage
import React from "react";
import { useAllotizeState } from "allotize-react";
export function Counter() {
const [allotize, setAllotize] = useAllotizeState({
route: "counter",
state: {
count: 0,
}
});
return (
<div>
{allotize.count}
<button onClick={() => setAllotize({...allotize, count: allotize.count += 1})}>+</button>
<button onClick={() => setAllotize({...allotize, count: allotize.count -= 1})}>-</button>
</div>
);
}
Allotize
?: optional
// Creates a shared state
Allotize.Crate({
// URI for the resource
route: 'String',
// Specifies if the resource should be persisted
persist: '?bool',
// Callback when a change is made to the resouce
onChange: '?function',
// Callback for when a local change is made to the resouce
onLocalChange: '?function',
// Callback for when a remote change is made to the resouce
onRemoteChange: '?function',
// List of callbacks to call when a change is made to the resouce
onChangeCallbacks: '?array[]function',
// Throttle interval for sending and syncing changes
throttleInterval: '?int (ms) [DEFAULT=350ms]',
// Data for your resouce
data: {
field1: 'value1',
field2: 'value2',
...,
fieldN: 'valueN',
}
})
// Returns connection information
Allotize.metadata()
// Returns all objects stored by Allotize
Allotize.get_all()
// Removes an item from Allotize by its key
Allotize.remove(key: 'String')
allotize-react
?: optional
import {useAllotizeState} from 'allotize-react';
const [allotize, setAllotize] = useAllotizeState({
route: "counter",
?config: {
// Specifies if the resource should be persisted
persist: '?bool',
// Callback when a change is made to the resouce
onChange: '?function',
// Callback for when a local change is made to the resouce
onLocalChange: '?function',
// Callback for when a remote change is made to the resouce
onRemoteChange: '?function',
// List of callbacks to call when a change is made to the resouce
onChangeCallbacks: '?array[]function',
// Throttle interval for sending and syncing changes
throttleInterval: '?int (ms) [DEFAULT=350ms]',
}
state: {
count: 0,
}
});