1
0
Fork
You've already forked celestejs
0
Minimal reactive state-based frontend framework for people who don't like JavaScript
  • JavaScript 100%
2026年02月27日 20:44:21 +01:00
examples add example of a music player and pocketbase usage 2026年02月27日 20:44:21 +01:00
celeste.js add show-if-current attribute (same kind of stuff as bind-current but for show) 2026年02月27日 20:44:10 +01:00
README.md oopsie i meant bytes not kb 2026年02月27日 00:54:31 +01:00

CelesteJS

CelesteJS is a very simple frontend framework in pure vanilla javascript, less than 100 lines of code and a compressed filesize of around ~500 bytes.

How it works?

This is a state-based framework, meaning instead of, for every event, having to handle all the side effects of all the elements that change on the page, you simply have to change the "state" (which is simply an object with whatever value you want).

Everytime the state is changed, the elements that depend on that state are updated.

This framework is heavily inspired by AlpineJS for how it works and by Reef for some part of its code.

To start CelesteJS you simply have to use the start function in which you declare the initial state, event handlers (for instance what you want to happen when a user clicks on a button) and side effects (those will get executed at every state change. You can use it to persist data, or compute derived state attributes for instance).

Overall CelesteJS is made for developpers who want to easily buid reactive apps but hate writing JavaScript and using heavy frameworks. So it's meant to stay as close to HTML as possible, but unlike alpineJS, to not include JavaScript within the HTML itself to keep it clean, readable and semantic.

The only thing you need to start is import celeste.js (like using a simple script tag or by copy-pasting the code). Then use the following start function:

let state = start({
 initialState: {}, // The initial state of the app
 events: {}, // The event handlers that will be called from `on` attributes in html. Each event handler function takes two input: the event and the state
 effects: [] // The functions to be ran on every change of state, each effect function takes the state as its argument
});
// from there you can modify the state after its initialization if you need to retrieve data from APIs for instance

HTML attributes

Pretty much everything first come using HTML attributes which serve to bind the state to the given element. There's not a lot of them and the amount of attributes available is kept deliberately simple.

  • bind attribute serves to bind an HTML attribute to a value in the state. In the example below every time the "message" in the state is changed, the innerText of the paragraph will be updated to match it.
<p bind="innerText:message"></p>
  • on attributes serves to bind an event on the element to an event handler (a Javascript function). In the example here, a click event on the button triggers the "sayHello" event handler.
<button on="click:sayHello">Click me!</button>
  • bind and on together serves to create a two-way bind, for instance for an input. In this example, every time the "message" is changed in the state, the value of the element will get updated. And everytime an "input" event is triggered from the input, the "message" in the state will get updated.
<input type="text" bind="value:message" on="input">
  • show-if serves to hide or display an element based on a boolean value in the state. In this example, the div will only be displayed if "isLoggedOut" in the state is truthy
<div show-if="isLoggedOut">Please sign in to access this page</div>
  • for-each and bind-current serves to repeat an element for each element of an array/iterable. In the following example, each element of messages will be inserted in <li> items.
<ul>
 <template for-each="messages">
 <li bind-current="innerText"></li>
 </template>
</ul>
  • Variant of for-each and bind-current. If each element in the loop is an object, we can bind an element to a key of that object. For instance here if we assume that messages is a list of objects that have keys "author" and "content", we can do the following:
<ul>
 <template for-each="messages">
 <li><b bind-current="innerText:author"></b> said '<i bind-current="innerText:content"></i>'</li>
 </template>
</ul>

Examples

You can find more concrete examples of how to use this library/framework in the examples directory of this project.