An easy to use, lightweight library for web-components.
Created a year ago, last commit 20 days ago
Number of contributors not available
227 commits
Stars added on GitHub, month by month
Stars added on GitHub, per day, on average
Last week
0.0 /day
Last month
+0.1 /day
Package on NPM
Monthly downloads on NPM
No dependencies


npm version Known Vulnerabilities License: MIT

What is bay.js?

Bay.js is a frontend library designed to facilitate the creation of reusable web-components, as well as to add state and interactivity to HTML. To see it in action, you can visit and explore its examples.

Key features

  • Optional build step: Simply add a script tag to get started.
  • Strict Content Security Policy (CSP) compatibility, even without a build step.
  • Versatile rendering: Regular DOM or web-component formats.
  • Zero dependencies for lightweight implementation.
  • Single-file bundling capability for streamlined components.
  • Seamless integration with frameworks designed for rendering web-components.
  • URL-based templates: Enable server-side rendering (SSR) or PHP-based templates.
  • Utilize <template> in the DOM as a template source.
  • Component creation from JavaScript strings for dynamic customization.
  • User-friendly syntax for effortless development.


Bay.js can be used via a script tag or importing as a module.

Script tag:

<script src="//"></script>

Script tag example

ES Module:

<script type="module" src="main.js"></script>
// In main.js
import bay from '//';

You may want to add a version number like this to prevent breaking changes: '//< VERSION >/bay.min.mjs' the version number is optional and can be acquired from going to the npm package page. Or if using node_modules: './node_modules/@dunks1980/bay.js/bay.min.mjs';
ES Module example

NPM Module:

npm i @dunks1980/bay.js
import bay from '@dunks1980/bay.js';

NPM example


import bay from ...

  text: `<h1 :style="margin: 1rem;">Settings</h1>`,
<template id="my-template">
  %{text} <!-- becomes: --> <h1 :style="margin: 1rem;">Settings</h1> 

<my-component bay="#my-template" message="Hello world!"></my-component>

Settings can only be set with ES or NPM modules, must be strings, and they can only be set once before creating components. Settings variables can be used anywhere within the templates.
Settings example


There are a few ways to define a component in bay.js:

1. Template with bay attribute in the DOM.

In your html create an inline template and apply bay attribute and any other props:

<template bay="my-component-name" message>

<my-component-name message="Hello world!"></my-component-name>

2. Template with id in the DOM.

In your html create an inline template and supply bay.js the templates id:

<template id="my-template">

<my-component bay="#my-template" message="Hello world!"></my-component>

3. Template in a file.

Create a file and supply bay.js the url (don't wrap file contents in template). The file extension can be anything you like as long as its contents are in the HTML format:

<my-component bay="/url/to/my/components.html" message="Hello world!"></my-component>
<!-- or -->
<my-component bay="/url/to/my/components.php" message="Hello world!"></my-component>
<!-- in component file -->

4. Create a template with JS.

Pass bay.js the imported template:

import my_component from "./../component_imports/my_component.html?raw";
import bay from "@dunks1980/bay.js";
bay.create("my-component", my_component, ["message"]);

Or create template and pass it: ( $ and ` within the string will need \ escaping if string literal)

import bay from "@dunks1980/bay.js";
bay.create("my-component", `<h1>\${this.message}</h1>`, ["message"]);
<!-- Anywhere in the HTML including other components -->
<my-component message="Hello world!"></my-component>

5. Using bay-hydrate.

In your html create an inline element with a template inside, then add bay-hydrate to the containing element:

<div bay-hydrate>
  <h1>This will be replaced once bay creates the component.</h1>
    <script>this.message = 'Hello World!'</script>

Props will not be available using this method, but you can use settings. Content is rendered to the main DOM so styles will not be encapsulated and <style> tags defined within the template will have no effect.

A component can be used anywhere in the HTML but inline templates must be in the body of the document. "my-component" can be anything you like but it must have a dash in the name per the custom element spec.

Tags in components:

Syntax Description Example
<if this="">...</if> If statement, renders only the content between <if> tags if its this attribute is true. Conditionals
<else-if this="">...</else-if> Else if, renders only the content between <else-if> tags if its this attribute is true and previous <if> <else-if> is false. Conditionals
<else>...</else> Else, renders only the content between <else> tags if all previous <if> <else-if> tags are false. Conditionals
<show this="">...</show> Show, when you need to keep the code in the DOM and toggle its display. Add duration <show this="" duration=".5s"> for a fade effect. Show
<switch this=""> <case this="xxx" break>...</case> <default>...</default> </switch> Switch statement, for more complex conditionals. Switch
<map array="this.arr">...</map> Map, to iterate over an array <map params="item, i, array" array="this.arr" join="<hr>"> params and join are optional, default params are: element, index, array. Iterations
<for array="this.arr">...</for> Foreach loop, to iterate over an array <for params="item, i, array" array="this.arr"> params are optional, default params are: element, index, array. Iterations
<for this="let i = 0; i < this.arr.length; i++">...</for> For loop, to iterate with conditions. Iterations
<inner-html>...</inner-html> To render to the Light DOM. If inner-html attribute is present on the component it will render inside that element:
<my-comp bay="..." inner-html="#render-target">. If inner-html attribute is not present on the component you can use a slot to see the inner-html content.
Inner HTML
<slot name="slot1">...</slot> Used to define a slot as per standard web-component. Slots
<route href="">...</route> Route creates the route for the router tag, is intended for a single page application (SPA). Route
<router this="/xxx/:xxx">...</router> Router matches the current url, :xxx are used for variables. Route
<include this="https://..."></include> Include code into template from a URL. Include

Internal Variables:

Syntax Description Example = 'xxx'; Assigning a value will trigger a bay component render.
To get/set this value from outside the component:
document.getElementById('my-el') = 'xxx';
$global = 'xxx'; Assigning $ a value will trigger render on all bay components that contain $global.
To get/set this value from outside the components: = 'hello'; console.log(;
$bay.querySelector('xxx'); Use $bay to target the Shadow DOM. Shadow DOM vs. Light DOM Variables
$el.querySelector('xxx'); Use $el to target the Light DOM. Shadow DOM vs. Light DOM Variables
$ = 'xxx'; Assigning $ a value will update in parent if the parent is another bay component and inturn triggers a render. Parent
Details from the slotchange script attribute as to what changed. Slots
$route Details from the window location. Router
$path :variables from the search path :xxx/:xxx. Router

Attributes On Component:

Syntax Description Example
When defined on the <custom-element bay="..."> tag it will be the template to use, can be a templates id or a path to a file, dsd is experimental. Using id
Using file
bay="my-template-name" When defined on a <template bay="custom-element"> it will be the <custom-element> tag name. Using name
fouc Used to hide the Light DOM within the component until it is fully loaded. FOUC
inner-html="#render-target" Used to tell the component where it should render <inner-html>...</inner-html> content. Inner HTML
xxx="value" Any other attributes are passed into the component and become props that can be accessed via, xxx being the attribute name. Props

Internal Attributes:

Syntax Description Example
:style="color: red; display: ${this.display}" Apply inlined data driven styles. Styles
:click="console.log('clicked')" Any javascript event that begins with on (onclick in this example) just replace on with : (oninput="xxx" -> :input="xxx"). Events
:my-event="console.log('my custom event')" Listens for any custom event and triggers code when it detects that event has been triggered from anywhere. Custom event
bind="" Used for 2-way data binding on <inputs>, <selects> and <textareas>. Bind
slot="slot-name" Used to define a slot as per standard web-component. Slots
ref="xxx" $ref('xxx').onclick = (e) => {...
See Internal Functions below for usage.
this-attribute="xxx" Will replace this-attribute="xxx" with whats returned from its attribute value. This Attribute

Script Lifecycle:

Syntax Description Example
<script imports>...</script> Define module import scripts. Imports
<script mounted>...</script> Script runs after template is mounted to DOM. Mounted
<script props>...</script> Script that runs when props change. Props
<script render>...\</script> Script that renders its return value in place. Render
<script slotchange>...</script> Script that runs on slot content changes. Slot Change
<script update>...</script> Script that runs on every update. Update

External Functions:

Syntax Description Example
bay(); Used to initialise bay.js if imported module. Installation
bay.create('component-name', '<h1>test</h1>', ["prop-1", "prop-2"]); Create a component. Create
bay.refresh(); Refresh bay after a new custom element is dynamically applied to the DOM. Refresh

Internal Functions:

Syntax Description Example
$bay.encode('string'); Encode/escape a string. Encode
$bay.decode('string'); Decode/un-escape a string. Decode
$bay.emit('custom-event', {key: value}); Emit a custom event. (across all components) Emit
$bay.on('custom-event', (e) => {console.log(e.detail);}); Listen for a custom event. (across all components) Emit
$ref('xxx').onclick = (e) => {...
$ref('xxx').forEach((el) => {...
Gets elements with the attribute ref="xxx". Can only be used in mounted script. Ref