Convert Figma logo to code with AI

tinyplex logotinybase

A reactive data store & sync engine.

5,165
134
5,165
14

Top Related Projects

20,137

Fluent UI web represents a collection of utilities, React components, and web components for building web applications.

91,260

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation

246,614

The library for web and native user interfaces.

38,777

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.

16,453

:fire: An extremely fast, React-like JavaScript library for building modern user interfaces

36,074

A declarative, efficient, and flexible JavaScript library for building user interfaces.

Quick Overview

TinyBase is a lightweight, reactive JavaScript library for structured state management. It provides a simple and efficient way to manage application state, with support for tables, indexes, and relationships. TinyBase is designed to be fast, flexible, and easy to use in both browser and Node.js environments.

Pros

  • Lightweight and performant, with a small bundle size
  • Reactive architecture for efficient updates and subscriptions
  • Supports complex data structures with tables, indexes, and relationships
  • TypeScript support for improved type safety and developer experience

Cons

  • Relatively new project, which may lead to potential instability or lack of community support
  • Limited ecosystem compared to more established state management solutions
  • Learning curve for developers unfamiliar with reactive programming concepts
  • May be overkill for simple applications with minimal state management needs

Code Examples

Creating a store and adding data:

import { createStore } from 'tinybase';

const store = createStore();
store.setTable('users', {
  1: { name: 'Alice', age: 30 },
  2: { name: 'Bob', age: 25 },
});

Subscribing to changes in the store:

store.addTableListener('users', (store, tableId, cellIds) => {
  console.log('Users table updated:', cellIds);
});

store.setCell('users', '3', 'name', 'Charlie');

Using indexes for efficient querying:

store.setIndex('usersByAge', 'users', 'age');
const youngUsers = store.getIndexIds('usersByAge', 25);
console.log('Users aged 25:', youngUsers);

Getting Started

To get started with TinyBase, follow these steps:

  1. Install TinyBase in your project:

    npm install tinybase
    
  2. Import and create a store:

    import { createStore } from 'tinybase';
    const store = createStore();
    
  3. Add data to the store:

    store.setTable('todos', {
      1: { text: 'Buy groceries', completed: false },
      2: { text: 'Walk the dog', completed: true },
    });
    
  4. Subscribe to changes and use the data in your application:

    store.addTableListener('todos', (store, tableId, cellIds) => {
      console.log('Todos updated:', store.getTable('todos'));
    });
    

For more advanced usage and features, refer to the official TinyBase documentation.

Competitor Comparisons

20,137

Fluent UI web represents a collection of utilities, React components, and web components for building web applications.

Pros of FluentUI

  • Comprehensive UI component library with a wide range of pre-built elements
  • Strong backing from Microsoft, ensuring regular updates and support
  • Extensive documentation and community resources

Cons of FluentUI

  • Larger package size and potential performance overhead
  • Steeper learning curve due to its extensive feature set
  • Less flexibility for custom styling compared to lightweight alternatives

Code Comparison

FluentUI:

import { PrimaryButton } from '@fluentui/react';

const MyComponent = () => (
  <PrimaryButton text="Click me" onClick={() => console.log('Clicked')} />
);

TinyBase:

import { createStore } from 'tinybase';

const store = createStore().setTable('users', {
  1: { name: 'Alice', age: 30 },
});

Summary

FluentUI is a robust UI library with a wide range of components, ideal for large-scale applications aligned with Microsoft's design language. It offers extensive documentation and community support but comes with a larger footprint and potential performance considerations.

TinyBase, on the other hand, is a lightweight state management solution focused on efficiency and simplicity. It's more suitable for projects requiring custom UI implementations or those prioritizing small bundle sizes and performance.

The choice between these libraries depends on project requirements, team expertise, and performance considerations.

91,260

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation

Pros of Storybook

  • Extensive ecosystem with a wide range of addons and integrations
  • Robust documentation and large community support
  • Supports multiple frontend frameworks (React, Vue, Angular, etc.)

Cons of Storybook

  • Steeper learning curve and more complex setup
  • Larger bundle size and potential performance overhead
  • May be overkill for smaller projects or simple component libraries

Code Comparison

Storybook:

// Button.stories.js
import { Button } from './Button';

export default {
  title: 'Components/Button',
  component: Button,
};

const Template = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  label: 'Button',
};

TinyBase:

// No direct equivalent in TinyBase as it's not a UI component library
// TinyBase focuses on data management, e.g.:
const store = createStore().setTables({
  users: {
    1: { name: 'Alice', age: 30 },
    2: { name: 'Bob', age: 25 },
  },
});

TinyBase and Storybook serve different purposes. Storybook is a UI component development and documentation tool, while TinyBase is a lightweight data management library. The code comparison highlights this difference, showing Storybook's focus on component stories and TinyBase's emphasis on data structures.

246,614

The library for web and native user interfaces.

Error generating comparison

38,777

⚛️ 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
  • Large ecosystem and community support

Cons of Preact

  • Fewer features compared to full React library
  • Some React libraries may not be fully compatible without adaptations
  • Learning curve for developers used to React's full feature set

Code Comparison

Preact:

import { h, render } from 'preact';

const App = () => <h1>Hello, World!</h1>;
render(<App />, document.body);

TinyBase:

import { createStore } from 'tinybase';

const store = createStore();
store.setTable('users', { 1: { name: 'John' } });

Key Differences

  • Preact focuses on UI rendering and component-based architecture
  • TinyBase is a data store management solution
  • Preact is more suitable for building user interfaces
  • TinyBase is better for managing application state and data

Use Cases

  • Preact: Web applications, especially those requiring a lightweight React-like library
  • TinyBase: Applications needing efficient data management and state synchronization

Community and Ecosystem

  • Preact has a larger community and more third-party libraries
  • TinyBase is newer with a growing ecosystem, focused on data management tools
16,453

: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
  • Extensive ecosystem with routing, state management, and server-side rendering
  • Familiar API for React developers, making migration easier

Cons of Inferno

  • Smaller community and fewer third-party libraries
  • Less frequent updates and potentially slower bug fixes
  • Limited TypeScript support compared to more mainstream frameworks

Code Comparison

Inferno:

import { render } from 'inferno';

function MyComponent({ name }) {
  return <div>Hello, {name}!</div>;
}

render(<MyComponent name="World" />, document.getElementById('app'));

TinyBase:

import { createStore } from 'tinybase';

const store = createStore();
store.setTable('users', { 1: { name: 'World' } });

console.log(store.getCell('users', '1', 'name')); // Outputs: World

Summary

Inferno is a high-performance React-like library focused on speed and efficiency, while TinyBase is a structured data store for JavaScript applications. They serve different purposes, with Inferno being a UI framework and TinyBase a data management solution. Inferno excels in rendering performance but has a smaller ecosystem, whereas TinyBase offers a lightweight and flexible approach to data storage but isn't directly comparable as a UI framework.

36,074

A declarative, efficient, and flexible JavaScript library for building user interfaces.

Pros of Solid

  • More mature and widely adopted project with a larger community
  • Comprehensive reactive framework for building user interfaces
  • Offers a virtual DOM-free approach for better performance

Cons of Solid

  • Steeper learning curve for developers new to reactive programming
  • Larger bundle size compared to TinyBase
  • May be overkill for simple applications or data management needs

Code Comparison

Solid example:

import { createSignal } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  return <button onClick={() => setCount(count() + 1)}>{count()}</button>;
}

TinyBase example:

import { createStore } from "tinybase";

const store = createStore().setTable("counters", { main: { value: 0 } });
store.setCell("counters", "main", "value", (v) => v + 1);

Summary

Solid is a full-featured reactive framework for building user interfaces, while TinyBase focuses on lightweight data management. Solid offers more comprehensive tools for UI development but may be more complex for simple use cases. TinyBase provides a simpler API for data handling but lacks built-in UI components. The choice between them depends on the specific needs of your project and the level of complexity you require.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Local first.

Sync on demand.

Fast always.

TinyBase is a reactive in-memory data store with persistence and synchronization for local-first JavaScript and TypeScript apps that need to feel instant, work offline, and stay consistent when they come back online.

NEW! v9.7 release

"The one with node:sqlite!"

Get started

Try the demos

Read the docs


Your first TinyBase app

Create a todo list, a chat app, a drawing tool, a charting app, or a tic-tac-toe game - with sync & persistence! - in less than 60s.

> npm create tinybase@latest

📦 Creating your project...

It's Reactive

TinyBase lets you listen to changes made to any part of your data. This means your app will be fast, since you only spend rendering cycles on things that change. The optional bindings to React, pre-built components, and charts let you easily build fully reactive UIs on top of TinyBase. You even get a built-in undo stack, and developer tools!

It's Database-Like

Consumer app? Enterprise app? Or even a game? Model key-value data and tabular data with optional typed schematization, whatever its data structures. There are built-in indexing, metric aggregation, and tabular relationships APIs - and a powerful query engine to select, join, filter, and group data (reactively!) without SQL.

It Synchronizes

TinyBase has native CRDT support, meaning that you can deterministically synchronize and merge data across multiple sources, clients, and servers. And although TinyBase is an in-memory data store, you can easily persist your data to file, browser storage, IndexedDB, SQLite or PostgreSQL databases, and more.

It's Built For A Local-First World

TinyBase works anywhere that JavaScript does, but it's especially great for local-first apps: where data is stored locally on the user's device and that can be run offline. It's tiny by name, tiny by nature: just 7.3kB - 16.4kB and with no dependencies - yet 100% tested, fully documented, and of course, open source!


TinyBase works great on its own, but also plays well with friends.

UI Frameworks

Storage & Sync

Schema Systems

(Baffled by all these logos? Check out our architectural options guide to make sense of it all!)




Start with a simple key-value store.

Creating a Store requires just a simple call to the createStore function. Once you have one, you can easily set Values in it by unique Id. And of course you can easily get them back out again.

Read more about using keyed value data in The Basics guide.

import {createStore} from 'tinybase';

const store = createStore()
  .setValues({employees: 3})
  .setValue('open', true);

console.log(store.getValues());
// -> {employees: 3, open: true}

Level up to use tabular data.

For other types of data applications, a tabular data structure is more useful. TinyBase lets you set and get nested Table, Row, or Cell data, by unique Id - and in the same Store as the keyed values!

Read more about setting and changing data in The Basics guide.

store
  .setTable('pets', {fido: {species: 'dog'}})
  .setCell('pets', 'fido', 'color', 'brown');

console.log(store.getRow('pets', 'fido'));
// -> {species: 'dog', color: 'brown'}

Register granular listeners.

The magic starts to happen when you register listeners on a Value, Table, Row, or Cell. They get called when any part of that object changes. You can also use wildcards - useful when you don't know the Id of the objects that might change.

Read more about listeners in the Listening To Stores guide.

const listenerId = store.addTableListener('pets', () =>
  console.log('changed'),
);

store.setCell('pets', 'fido', 'sold', false);
// -> 'changed'

store.delListener(listenerId);

Bind to data in UI libraries.

If you're using React, Solid, or Svelte in your application, the optional ui-react module, ui-solid module, or ui-svelte module provide hooks and functions to bind to the data in a Store.

For example, the useCell hook in this example fetches the dog's color. But it also registers a listener on that cell that will fire and re-render the component whenever the value changes!

Basically you simply describe what data you want in your user interface and TinyBase will take care of the whole lifecycle of updating it for you.

Read more about the using hooks in the Using React Hooks guide and the Building UIs With Svelte guide.

import React from 'react';
import {createRoot} from 'react-dom/client';
import {useCell} from 'tinybase/ui-react';

const App1 = () => {
  const color = useCell('pets', 'fido', 'color', store);
  return <>Color: {color}</>;
};

const app = document.createElement('div');
const root = createRoot(app);
root.render(<App1 />);
console.log(app.innerHTML);
// -> 'Color: brown'

store.setCell('pets', 'fido', 'color', 'walnut');
console.log(app.innerHTML);
// -> 'Color: walnut'

root.unmount();

Pre-built reactive components.

The UI modules also provide bare React, Solid, and Svelte components that let you build up a fully reactive user interface based on a Store.

It's super easy to wire up tabular display of your data with lots of customization and interactivity options.

Try them out in the UI Components (React), Chart Components (React), UI Components (Solid), and UI Components (Svelte) demos, and read more in the Building UIs guides.

Local-first data visualizations.

Render TinyBase data and queries directly as responsive, styleable SVG charts with the ui-react-dom-charts module. These charts update reactively when data changes, and are composable with labels, bounds, ticks, and time-aware x-axes.

Try the Chart Components (React) demos, or read more in the Using Charts guide.

An inspector for your data.

If you are building a web application with React or Svelte, the Inspector component lets you overlay a view of the data in your Store, Indexes, Relationships, and so on. You can even edit the data in place and see it update in your app immediately.

Read more about this powerful new tool in the Inspecting Data guide.

Apply schemas to tables & values.

Want clean data? You can add a ValuesSchema or a TablesSchema to a Store to ensure that they are always what you expect by constraining types, and providing defaults. There are even TypeScript definitions that infer API types from the data schemas you apply.

And our 'schematizer' modules let you convert schemas from libraries like Zod, TypeBox, Valibot, ArkType, Yup, and Effect Schema into TinyBase at runtime.

Read more about schemas in the Schemas guide.

store.setTablesSchema({
  pets: {
    species: {type: 'string'},
    color: {type: 'string'},
    sold: {type: 'boolean', default: false},
  },
});

store.setRow('pets', 'polly', {species: 'parrot'});
console.log(store.getRow('pets', 'polly'));
// -> {species: 'parrot', sold: false}

store.delTablesSchema();

Synchronize between devices.

The MergeableStore type acts as a native CRDT, letting you merge data and synchronize it between clients and systems - or even a server. The synchronization protocol can run over WebSockets, the browser BroadcastChannel, or your own custom synchronization medium.

Read more about these techniques in the Synchronization guides.

import {WebSocketServer, WebSocket} from 'ws';
import {createMergeableStore} from 'tinybase';
import {createWsServer} from 'tinybase/synchronizers/synchronizer-ws-server';
import {createWsSynchronizer} from 'tinybase/synchronizers/synchronizer-ws-client';

// On a server machine:
const server = createWsServer(
  new WebSocketServer({port: 8040}),
);

// On a client machine:
const store1 = createMergeableStore();
const synchronizer1 = await createWsSynchronizer(
  store1,
  new WebSocket('ws://localhost:8040'),
);
await synchronizer1.startSync();

// ...

await synchronizer1.destroy();
await server.destroy();

Persist to storage or a database.

You can easily persist a Store between browser page reloads or sessions. You can also synchronize it with a web endpoint, or (if you're using TinyBase in an appropriate environment), load and save it to a file. You can bind TinyBase to various flavors of database, or to Yjs and Automerge CRDT documents.

Read more about persisters in the Persistence guides.

import {createSessionPersister} from 'tinybase/persisters/persister-browser';

const persister = createSessionPersister(store, 'demo');
await persister.save();

console.log(sessionStorage.getItem('demo'));
// ->
`
[
  {
    "pets":{
      "fido":{"species":"dog","color":"walnut","sold":false},
      "polly":{"species":"parrot","sold":false}
    }
  },
  {"employees":3,"open":true}
]
`;

await persister.destroy();
await sessionStorage.clear();

Build complex queries with TinyQL.

The Queries object lets you query data across tables, with filtering and aggregation - using a SQL-adjacent syntax called TinyQL.

Accessors and listeners let you sort and paginate the results efficiently, making building rich tabular interfaces easier than ever.

In this example, we have two tables: of pets and their owners. They are joined together by the pet's ownerId Cell. We select the pet's species, and the owner's state, and then aggregate the prices for the combinations.

We access the results by descending price, essentially answering the question: "which is the highest-priced species, and in which state?"

Needless to say, the results are reactive too! You can add listeners to queries, and since v7.2, even parameterize them.

Read more about Queries in the v2.0 Release Notes, the Using Queries guide, and the Car Analysis demo and Movie Database demo.

import {createQueries} from 'tinybase';

store
  .setTable('pets', {
    fido: {species: 'dog', ownerId: '1', price: 5},
    rex: {species: 'dog', ownerId: '2', price: 4},
    felix: {species: 'cat', ownerId: '2', price: 3},
    cujo: {species: 'dog', ownerId: '3', price: 4},
  })
  .setTable('owners', {
    1: {name: 'Alice', state: 'CA'},
    2: {name: 'Bob', state: 'CA'},
    3: {name: 'Carol', state: 'WA'},
  });

const queries = createQueries(store);
queries.setQueryDefinition(
  'prices',
  'pets',
  ({select, join, group}) => {
    select('species');
    select('owners', 'state');
    select('price');
    join('owners', 'ownerId');
    group('price', 'avg').as('avgPrice');
  },
);

queries
  .getResultSortedRowIds('prices', 'avgPrice', true)
  .forEach((rowId) => {
    console.log(queries.getResultRow('prices', rowId));
  });
// -> {species: 'dog', state: 'CA', avgPrice: 4.5}
// -> {species: 'dog', state: 'WA', avgPrice: 4}
// -> {species: 'cat', state: 'CA', avgPrice: 3}

queries.destroy();

Define metrics and aggregations.

A Metrics object makes it easy to keep a running aggregation of Cell values in each Row of a Table. This is useful for counting rows, but also supports averages, ranges of values, or arbitrary aggregations.

In this example, we create a new table of the pet species, and keep a track of which is most expensive. When we add horses to our pet store, the listener detects that the highest price has changed.

Read more about Metrics in the Using Metrics guide.

import {createMetrics} from 'tinybase';

store.setTable('species', {
  dog: {price: 5},
  cat: {price: 4},
  worm: {price: 1},
});

const metrics = createMetrics(store);
metrics.setMetricDefinition(
  'highestPrice', // metricId
  'species', //      tableId to aggregate
  'max', //          aggregation
  'price', //        cellId to aggregate
);

console.log(metrics.getMetric('highestPrice'));
// -> 5

metrics.addMetricListener('highestPrice', () =>
  console.log(metrics.getMetric('highestPrice')),
);
store.setCell('species', 'horse', 'price', 20);
// -> 20

metrics.destroy();

Create indexes for fast lookups.

An Indexes object makes it easy to look up all the Row objects that have a certain value in a Cell.

In this example, we create an index on the species Cell values. We can then get the the list of distinct Cell value present for that index (known as 'slices'), and the set of Row objects that match each value.

Indexes objects are reactive too. So you can set listeners on them just as you do for the data in the underlying Store.

Read more about Indexes in the Using Indexes guide.

import {createIndexes} from 'tinybase';

const indexes = createIndexes(store);
indexes.setIndexDefinition(
  'bySpecies', // indexId
  'pets', //      tableId to index
  'species', //   cellId to index
);

console.log(indexes.getSliceIds('bySpecies'));
// -> ['dog', 'cat']
console.log(indexes.getSliceRowIds('bySpecies', 'dog'));
// -> ['fido', 'rex', 'cujo']

indexes.addSliceIdsListener('bySpecies', () =>
  console.log(indexes.getSliceIds('bySpecies')),
);
store.setRow('pets', 'lowly', {species: 'worm'});
// -> ['dog', 'cat', 'worm']

indexes.destroy();

Model table relationships.

A Relationships object lets you associate a Row in a local Table with the Id of a Row in a remote Table. You can also reference a table to itself to create linked lists.

In this example, the species Cell of the pets Table is used to create a relationship to the species Table, so that we can access the price of a given pet.

Like everything else, you can set listeners on Relationships too.

Read more about Relationships in the Using Relationships guide.

import {createRelationships} from 'tinybase';

const relationships = createRelationships(store);
relationships.setRelationshipDefinition(
  'petSpecies', // relationshipId
  'pets', //       local tableId to link from
  'species', //    remote tableId to link to
  'species', //    cellId containing remote key
);

console.log(
  store.getCell(
    relationships.getRemoteTableId('petSpecies'),
    relationships.getRemoteRowId('petSpecies', 'fido'),
    'price',
  ),
);
// -> 5

relationships.destroy();

Set checkpoints for an undo stack.

A Checkpoints object lets you set checkpoints on a Store. Move forward and backward through them to create undo and redo functions.

In this example, we set a checkpoint, then sell one of the pets. Later, the pet is brought back to the shop, and we go back to that checkpoint to revert the store to its previous state.

Read more about Checkpoints in the Using Checkpoints guide.

import {createCheckpoints} from 'tinybase';

const checkpoints = createCheckpoints(store);

store.setCell('pets', 'felix', 'sold', false);
checkpoints.addCheckpoint('pre-sale');

store.setCell('pets', 'felix', 'sold', true);
console.log(store.getCell('pets', 'felix', 'sold'));
// -> true

checkpoints.goBackward();
console.log(store.getCell('pets', 'felix', 'sold'));
// -> false

Did we say tiny?

If you use the basic store module alone, you'll only add a gzipped 7.3kB to your app. Incrementally add the other modules as you need more functionality, or get it all for 16.4kB.

The optional ui-react module is just 5.9kB, the ui-react-dom components are another 4.3kB, and everything is super fast. Life is easy when you have zero dependencies!

Read more about how TinyBase is structured and packaged in the Architecture guide.

 Minified .js.gzSource .js
tinybase/store (minimal)7.3kB79.8kB
tinybase (complete)16.4kB187.8kB
ui-react5.9kB64.2kB
ui-react-dom4.3kB38.4kB
ui-react-dom-charts8.7kB64.9kB
ui-solid5.8kB63.2kB
ui-solid-dom4.9kB48.0kB
ui-svelte5.1kB62.5kB
ui-svelte-dom6.0kB61.7kB

Well tested and documented.

TinyBase has 100.0% test coverage, including the code throughout the documentation - even on this page! The guides, demos, and API examples are designed to make it as easy as possible for you to get your TinyBase-powered app up and running.

Read more about how TinyBase is tested in the Unit Testing guide.

 TotalTestedCoverage
Tested lines5,1175,117100.0%
Tested statements5,5375,537100.0%
Tested functions2,4282,428100.0%
Tested branches1,7911,791100.0%
Tests9,658
Assertions40,119

Proud to be supported by:

Excited to be used by:


Get started

Try the demos

Read the docs


Meet the family

TinyBase is part of a group of small libraries designed to help make rich client and local-first apps easier to build. Check out the others!


Synclets

An open, storage-agnostic, sync engine development kit.


TinyWidgets

A collection of tiny, reusable, UI components.


TinyTick

A tiny but very useful task orchestrator.


About

Modern apps deserve better. Why trade reactive user experiences to be able to use relational data? Or sacrifice features for bundle size? And why does the cloud do all the work anyway?

Building TinyBase was originally an interesting exercise for me in [API](/api/) design, minification, and documentation. But now it has taken on a life of its own, and has grown beyond my wildest expectations.

It could not have been built without these great projects and friends, and I hope you enjoy using it as much as I do building it!

The story

NPM DownloadsLast 30 Days