std-components
v1.0.0-beta.1
Published
Standard HTML components as functions
Downloads
44
Maintainers
Readme
std-components
- 🌳 Composable, typed, tree shakeable functions that create standard DOM components.
- ⚡ No build step required.
- 🚀 Speed up the development of dynamic, performant, component-based front-end applications with vanilla JS/TS.
Install
pnpm i std-componentsExamples
👉 See /examples for full examples.
Blog post
import { article, header, h2, p } from 'std-components';
const post = article( { class: 'post-card' },
header( { class: 'post-header' },
h2( { class: 'post-title' }, 'First Post' )
),
div( { class: 'post-content' },
p( {}, 'Hello, world!' )
)
);
document.body.append( post );produces a <body> with:
<article class="post-card">
<header class="post-header">
<h2 class="post-title">First Post</h2>
</header>
<div class="post-content">
<p>Hello, world!</p>
</div>
</article>To-do-list
Suppose an HTML like this:
<h1>To-Do-List</h1>
<table>
<thead>
<tr>
<th>#</th>
<th>Description</th>
<th>Done</th>
</tr>
</thead>
<tbody></tbody>
</table>Creating rows can be like this:
import { tr, td, fragment } from 'std-components';
const toDoList = [
{ id: 1, description: 'Buy beer', done: false },
{ id: 2, description: 'Buy milk', done: true },
{ id: 3, description: 'Take the dog for a walk', done: false },
// ...
];
function toDoListRow( { id, description, done } ) {
return tr( {}
td( {}, id ),
td( {}, description ),
td( {}, done ? 'Yes' : 'No' ),
);
}
const rows = toDoList.map( toDoListRow );
document.querySelector( 'tbody' ).append( fragment( ...rows ) ); // Avoid DOM reflowThat will produce rows such as this:
<tr>
<td>1</td>
<td>Buy beer</td>
<td>No</td>
</tr>Easy-peasy, right? 😉
Now suppose that you need to toggle the "Done" value when the user clicks on "Yes" or "No".
Just define a click event in the corresponding td element, through the special property events :
td( { events: { click: toggleDone } }, done ? 'Yes' : 'No' ),where the function toggleDone can be a usual event listener that updates the To-Do object and the DOM:
function toggleDone( event ) {
const td = event.target;
const toDo = toDoList[ td.parentElement.sectionRowIndex ];
toDo.done = ! toDo.done; // Toggle
td.textContent = toDo.done ? 'Yes' : 'No'; // Refresh the row value
}Reactivity
If you need reactivity, use std-components with a library such as @preact/signals-core, S.js or usignal - that are not tied to a UI library/framework.
Example with @preact/signals-core:
import { button } from 'std-components';
import { signal, effect } from '@preact/signals-core';
// count starts at 0
const count = signal( 0 );
// When clicked, the button increments the count
const btn = button( { events: { click: () => count.value++ } } );
// When count changes, generates an effect that updates the button text
effect( () => {
btn.textContent = `Clicked ${count.value} times`
} );
document.body.append( btn );API
- The API covers almost all standard DOM elements , except deprecated ones.
- The functions have the same name as the HTML tags (except for
var_, sincevaris a reserved word in JavaScript). - Just import the desired functions and use them like in the example above.
Basic overall syntax:
function tag( props: {[key: string]: any} = {}, ...children: Array<string|Node|HTMLElement> ): HTMLElementwhere:
tagis the desired tag (function), likebutton;props(optional) is an object with DOM attributes you want to define in the tag;- Example:
div( { class: 'card' } )
- Example:
children(optional) are all child elements, separated by comma.- Example:
article( { class: 'post' }, header( {}, title( {}, 'First Post' ) ), p( {}, 'Hello, world!' ) )
- Example:
Special properties
eventsis an object that allows to define standard DOM events for the element.- Examples:
// Single event, single listener function button( { events: { click: () => alert('Hi') } }, 'Say Hi' ); // Multiple events, single function in each input( { events: { focus: event => console.log( `Enter ${event.target}` ), blur: event => console.log( `Left ${event.target}` ) } } ); // Single event, single function with options - same options as addEventListener's button( { events: { click: { listener: () => alert('Hi'), options: { once: true } } } }, 'Say Hi' ); // Single event, multiple functions button( { events: { click: [ ()=>alert('Hi'), ()=>alert('Hi again') ] } }, 'Say Hi' ); // Multiple events, multiple functions with or without options - same options as addEventListener's button( { events: { click: [ () => alert('Hi'), // First listener { listener: ()=>alert('Hi again'), options: { once: true } } // Second listener ], mouseover: (event) => console.log( 'Mouse is over', event.target ) } }, 'Say Hi' );
- Examples:
isis a special property that makes a standard HTML element behave like a defined customized built-in element. See MDN for more.
Extra functions
fragment( ...children: Array<string|Node|HTMLElement> ): DocumentFragmentcreates a DocumentFragment. It's very useful to avoid DOM reflow.- Example:
const users = await getUsers(); const tableRows = users.map( u => userToTableRow( u ) ); // All table rows will be rendered together, only once: document.querySelector( 'tbody' ).append( fragment( ...tableRows ) );
- Example:
text( value: string = '' ): Textcreates a text node. Usually not needed, since other functions accept a string as children.- Example:
const btn = button( {}, text( 'Ok' ) ); // Same as button( {}, 'Ok' )
- Example:
component< T extends HTMLElement >( tag: string, props: {[key: string]: any} = {}, ...children: Array<string|Node|HTMLElement> ): Tcreates any DOM component. You probably won't need to use it.- Example:
const btn = component( 'button', {}, 'Ok' ); // Same as button( {}, 'Ok' )
- Example:
