DEV Community

Cover image for Signalize.js - Introduction to Modular Javascript Framework
Vladimír Macháček
Vladimír Macháček

Posted on

Signalize.js - Introduction to Modular Javascript Framework

Signalize is a client-side JavaScript framework designed for swift web development with minimal JavaScript.

Signalize:

  • Leverages modern ES modules and import maps.
  • Imports only a small 2 KB core, allowing you to decide what to import and when.

The goal is to provide functionality similar to modern frameworks like Vue, Svelte, Solid, and Qwik, but with minimal JavaScript, the smallest possible learning curve, a simple codebase, no dependencies, and no need for a JavaScript backend.

Signalize in a Nutshell

Let's start with a simple example of how Signalize works:

  • Signalize is split into several JavaScript modules.
  • Each module provides different functionality.
  • To use Signalize, you need to define an import map first.
  • Then, you import the core and create a new Signalize instance.
  • This new instance will provide you with the resolve function for loading modules.
  • Every time you need some functionality, you import it using the resolve function.
  • Signalize internally reuses modules and automatically loads dependencies in the correct order to keep the framework small.
<!-- 1. Configure the importmap -->
<script type="importmap">
{
  "imports": {
    "signalizejs": "https://cdn.jsdelivr.net/npm/signalizejs@latest/+esm",
    "signalizejs/mutation-observer": "https://cdn.jsdelivr.net/npm/signalizejs@latest/mutation-observer/+esm",
    "signalizejs/dom/ready": "https://cdn.jsdelivr.net/npm/signalizejs@latest/dom/ready/+esm",
    "signalizejs/event": "https://cdn.jsdelivr.net/npm/signalizejs@latest/event/+esm"
  }
}
</script>
<script type="module">
    // 2. Import the Signalize core
    import Signalize from 'signalizejs';

    // 3. Create a new Signalize instance
    const { resolve } = new Signalize();

    // 4.1 Resolve the Event module because we need the "on" function
    // 4.2 The Event module will automatically import the Mutation Observer module
    const { on } = await resolve('event', 'dom/ready');

    // 5. Use the on function
    on('dom:ready', () => alert('Hello World!'));
</script>
Enter fullscreen mode Exit fullscreen mode

The Ecosystem

The Signalize ecosystem currently contains 25 modules focused on solving web development problems such as reactivity, web components, Ajax, SPA, error logging, and various utilities. The number of modules will definitely grow.

Here is a list of a few of them:

The Mindset

The basic idea is to import only what you need. Not everything needs to be a complex app with tons of features:

  • I need some simple reactivity: Use the Signals and Binding modules.
  • I have a carousel with dynamic products: Use the Ajax module and manually redraw Snippets.
  • I need reusable components: Use Web Components
  • I need more complex forms: Import Directives
  • I want to turn my website into an SPA: Add the SPA module.

A simple example with Signals & Reactivity.

You can edit this example in the playground

<button>
  Count: <span></span>
</button>

<script type="importmap">
{
  "imports": {
    "signalizejs": "https://cdn.jsdelivr.net/npm/signalizejs/+esm",
    "signalizejs/bind": "https://cdn.jsdelivr.net/npm/signalizejs/bind/+esm",
    "signalizejs/event": "https://cdn.jsdelivr.net/npm/signalizejs/event/+esm",
    "signalizejs/mutation-observer": "https://cdn.jsdelivr.net/npm/signalizejs/mutation-observer/+esm",
    "signalizejs/scope": "https://cdn.jsdelivr.net/npm/signalizejs/scope/+esm",
    "signalizejs/signal": "https://cdn.jsdelivr.net/npm/signalizejs/signal/+esm"
  }
}
</script>

<script type="module" defer>
    import Signalize from 'signalizejs';

    const { resolve } = new Signalize();

    // You code goes here
    const { signal, bind } = await resolve('signal', 'bind');

    const number = signal(0);

    document.querySelector('button').addEventListener(
      'click',
      () => number(number() + 1)
    );
    bind(document.querySelector('span'), { text: number });
</script>
Enter fullscreen mode Exit fullscreen mode

A simple example with Astro.build

Do you like Astro.build? It converts JavaScript into ES modules, so Signalize works very well with Astro. I wrote a guide on how to use Signalize in Astro.

Bellow is a simplified example:

1 - Install Signalize:

npm i signalizejs
Enter fullscreen mode Exit fullscreen mode

2 - Create src/assets/layout.js

import Signalize from 'signalizejs';

export const signalize = new Signalize();
Enter fullscreen mode Exit fullscreen mode

3 - Create src/layouts/main.astro

---
// 1. Get signalizejs url
import signalize from 'signalizejs?url';

// 2. Get event module url. This applies for all modules you will need.
import eventModuleUrl from 'signalizejs/event?url';

// 3. Define importmap
const importMap = JSON.stringify({
    imports: {
        // 4. Configure necessary modules
        'signalizejs/event': eventModuleUrl,
        // Other modules you need
    }
});
---

<html>
    <head>
        <!-- 5. Embed the import map -->
        <script type="importmap" :html={importMap}></script>
        <!-- 6. Create a global Signalize instance -->
        <script>
            import "../assets/layout.js";
        </script>
    </head>
    <body>
        <slot />
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

4 - Create src/pages/index.astro

---
// src/pages/index.astro
import MainLayout from '../layouts/main.astro';
---
<MainLayout>
    <script>
        import { signalize } from '../assets/layout.js';

        const { on } = await signalize.resolve('event');
    </script>
</MainLayout>
Enter fullscreen mode Exit fullscreen mode

Let me know what you think!

If you like the idea, let me know by starring the Signalize repo.

I welcome any feedback because Signalize is a new library and there is definitely room for improvement. 🙂

Stay in Touch!

I plan to write about the reactivity inspired by Solid and Angular Signals, the bindings and directives inspired by Svelte, and the ES module loader for dependency injection inspired by Qwik's loader.

If you want to get inspired for your next project or learn something new, it's a great place to start.


Top comments (10)

Collapse
 
intermundos profile image
intermundos

Looks very interesting, particularly for using with Astro.

Do you have more examples of using signalize with Astro?

Thanks!

Collapse
 
machy8 profile image
Vladimír Macháček • Edited

Hi. Thanks!

In which examples you would be interested in?

I use it on my websites but they run in Symfony (prace.dev) and Go (travellingvisio.com) so not probably what you have in mind.

Collapse
 
intermundos profile image
intermundos

Also a little Vite plugin would be helpful to intialize and expose singnilize globally. Will look into it now :)

Collapse
 
intermundos profile image
intermundos

Thanks for your reply!

Can singnilize substitute state management to share state across pages and componentes?

Thread Thread
 
machy8 profile image
Vladimír Macháček

Hi!

Globals
Every Signalize instance has global object within (mentioned in configuration section).
When you want to provide global data for "everything" within the instance, you put the data here. This way it is not poluting the window object and globals are automatically passed for example into directives.

export const mySignalizeInstance = new Signalize({
  globals: { /* Your Data */}
});

mySignalizeInstance.globals.someData = { /* Any data you want */}

// Somewhere else in the app
import { mySignalizeInstance } from 'app.js';

const { component } = await mySignalizeInstance.resolve('component');

component('my-component', () => {
  const { someData } = mySignalizeInstance.globals;
})
Enter fullscreen mode Exit fullscreen mode

This way you can basically create a "store" using signals and a simple class for example for a shopping cart.

Sharing State

  • Sharing state across pages is possible only with SPA, so the data you put dynamically into the globals are not reseted on every request

Sharing data between components
Yes

Unlike in Alpine, you must define properties and pass data to child component. The flow is from top to down. It's a bit more work but you know from where you got the data.

Thread Thread
 
intermundos profile image
intermundos

Thank you for reply. Will try this in Astro, though not sure globals will persist between pages.

Many thanks 🙏

Thread Thread
 
machy8 profile image
Vladimír Macháček

If you use the SPA module, it will redraw snippets instead of performing a full page reload.

Do you want to keep global variables without using the SPA module? Because if so, Signalize doesn't have any mechanism for that. I can't think of any safe way to achieve this either. The only possible safe solution would be to keep data on the server side.

Thread Thread
 
intermundos profile image
intermundos

Another way I think of is to serialize/deserialize state via storage api (index db) and use it across Astro pages and components.

Will try the SPA mode 🙏

Thread Thread
 
machy8 profile image
Vladimír Macháček

Yes, that's possible, but only for secure data with no personal information. Cleaning data becomes complicated when the browser window closes. It would need to be tied to a session with expiration timers in place.

I didn't need that functionality yet, but if the demand for this module arises from more people, I may look into it further.

Thread Thread
 
intermundos profile image
intermundos

Sure, depending on the need and use case. Syncing state via storage might provide an easy option to share the state.

User data can be set via Astro middleware to locals object on each request, while signalize can be client state layer.

Hopefully get spare time to experiment soon enough 🤞