⚡️ 🤖 Bolt
A light weight library ( 3k gzipped ) that gives you the speed and control of an SPA while keeping your site static through progressive enhancement and modern techniques.
Demo: https://bolt.oddcommon.dev
The primary goal of Bolt is to speed up page load times and give users the feel of an SPA while remaining static HTML. It's not quite a router and not quite a transition library, it's main objective is speed and progressive enhancement, it's up to you to decide how to implement.
You can use Bolt in a few different ways, out of the box it will improve load times with a few modifications to your code.
Heavily inspired by Quicklink, Turbo & Highway the goal of Bolt is to be an easy to integrate script that gives speed + flexibility to build around.
📦 Setup
To Install: npm i @oddcommon/bolt
Bolt is a long lived script and needs to run on each page and between renders. If you initialize Bolt from a script that script will be considered protected, that means it will never be disposed of.
ex: App.js
import Router from "@oddcommon/bolt";
class App {
constructor(){
this.BoltRouter = new Router();
this.observe();
}
observe(){
this.BoltRouter.on('navigate-before', () => { .... })
}
}
new App();
Dev
npm run dev
- http://localhost:8000/examples
Basics
Wrapper
In order for Bolt to know where updates need to be rendered you need to specify a dom element to use. This is done by adding data-bolt
to any top level DOM element that will be changing from page to page. Typically this would be your main content.
Anything outside of data-bolt
will be left intact between page renders. You can have multiple wrappers by specifying an ID
example:
<div data-bolt="some-id">...</div>
Link
To get Bolt running on your links just add data-bolt-link
as an attribute to any links. Bolt will handle the rest.
You can optionally set data-bolt-link="static"
and bolt will retain the current scroll position on the page between renders. This is helpful if you're paginating content and don't want to reset the user on every click. The default behavior is to scroll the user to the top of the page on every render.
Static
Adding data-bolt-static='static-id'
to an element will freeze that element and Bolt will not touch it between renders. These elements will never be removed or changed, you can treat them as independent sandboxes for long lived content.
✨ Super charge Bolt
Bolt has a predefined lifecycle and emits events throughout this lifecycle. you can tap into any of them at any time.
🎟 Events
- prefetch-before
- prefetch-complete
- navigate-before
- params:
{to: string, from: string}
- params:
- navigate-complete
- params:
{to: string, from: string}
- params:
- loading
- load-event
- params:
{total: integer, complete: integer}
- params:
- load-complete
- render-before
- render-complete
- bolt-complete
Merge Elements
Bolt has a concept of merging static elements that are outside the main data-bolt
wrapper. By adding data-bolt-merge
to an element bolt will merge attributes of those elements. This is handy if you have different classes or require a specific layout between pages on elements that are static.
example:
<div data-bolt-merge="div-id">...</div>
Prefetch
If you want to really go after speed and have specific assets that you want to preload before a navigation event even happens you can use data-bolt-prefetch
. This will add preload
tags and the browser will begin preloading those assets in the background.
API
Bolt has a very simple API that allows you the ability to control lifecycle steps.
-
BoltRouter.pause()
- Pauses execution before the next step in lifecycle -
BoltRouter.resume()
- Resumes execution -
BoltRouter.initialize()
- Refreshes bindings -
BoltRouter.disable()
- Disable Bolt
🚨 Danger Zone Be Very careful with these
-
BoltRouter.lock()
- Override the lock that Bolt creates during routing ( similar to pause but blocks any new routing from happening ) -
BoltRouter.unlock()
- Unlock the override
Transitions
When you initialize Bolt you can supply a transitions object where you can define transition functions and then specify them using data-bolt-transition="transitionName"
Your transition function will be called on navigate-before
- it us up to you to hook into the Bolt lifecycle. In the below example we are simply adding an await to delay, it's up to you to decide what happens and when to resume Bolt lifecycle. You can also hook into any step of the lifecycle for your transition.
Example:
import Router from '@oddcommon/bolt';
class App {
constructor() {
const exampleTransition = this.exampleTransition;
this.BoltRouter = new Router({
transitions: [
{
name: 'exampleTransition',
transition: exampleTransition,
},
],
});
}
// Bolt provides the to and from in the callback
async exampleTransition({ to, from }) {
// Pause Bolt lifecycle
this.BoltRouter.pause();
// ... Fancy transition animation and logic
await setTimeout(() => {
new Promise(resolve => resolve());
}, 500);
// Resume Bolt Lifecycle
this.BoltRouter.resume();
}
}
new App();
Register Transitions
You can register new transitions at any time by calling BoltRouter.registerTransition({ ...transition-object })
;
Transition Object Options
If you want to specify specific transitions based on pages you can define to
and from
in your transition object, Bolt will call the transiton whenever it's navigating to and from those specific pages.
const transitionObject = {
name: 'homeToAbout',
to: '/about/',
from: '/home/',
transition: exampleTransition,
};
You can also use regex to apply transitions to groups of pages.
BoltRouter = new Router({
transitions: [
{
name: 'exampleTransition',
from: /features/,
to: /link-example/,
transition: exampleTransition,
},
],
});
Examples
You can see a demo of Bolt in example
. Just run npm run dev
from the root of the Bolt project and the demo will start up.