npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@st-lib/render

v1.0.11

Published

Lightweight HTML rendering library.

Downloads

10

Readme

Lightweight HTML rendering library.

ESNext module, please use webpack / rollup / parcel / etc.

Additional materials

  1. @st-lib/render-html,
  2. @st-lib/render-svg,
  3. @st-lib/render-mathml,
  4. @st-lib/render-events,
  5. @st-lib/render-with-state.

Concepts.

  1. The library uses a context-oriented approach to describing the structure of HTML.
  2. The purpose of the library is NOT to work with the state of the application, use third-party libraries for this.
  3. The library does not create intermediate representations of the document structure (Virtual DOM), but instead updates the document directly.

Basic usage.

Use render function for render to document.

export interface Renderer {
    createElement(tagName: string, options?: ElementCreationOptions): Element;
    createElementNS(namespaceURI: string | null, tagName: string, options?: ElementCreationOptions): Element;
    createTextNode(data: string): Text;
    createComment(data: string): Comment;
    createAttribute(localName: string): Attr;
    createAttributeNS(namespace: string | null, qualifiedName: string): Attr;
}
export declare function render<T extends Element>(target: T | null, content: (ref: T) => void, renderer?: Renderer): void;

Use rerender function for render element with last used content callback and renderer

render(document.body, () => {
	let i = 0
	element(null, 'button', ref => {
		// see https://www.npmjs.com/package/@st-lib/render-events
		onClick(() => {
			i++
			rerender(ref)
		})
		text(null, i)
	})
})

Element

Create element node with specified key, tag name, namespace URI, creation options and content rendering function or raw HTML string

UPDATE: added customized built-in elements support.

export declare function element<T extends Element>(key: any, tag: string | [string, (string | null)?, (ElementCreationOptions | null)?], content?: ElementContentDescriptor<T> | false | null | undefined): T | null;

Key: accepts any type: number, string, symbol or object. Passing null or undefined as key will be replaced with current key order.

Tag: accepts tagName: string or turple[tagName: string, namespaceURI?: string | null, options?: ElementCreationOptions | null]

Content: content rendering function (ref: T extends Element) => void or raw HTML string

Returns the created element node or null if the node is not created.

Example:

import { render, element } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		element(null, 'header')
		element(null, 'main', () => {
			element(null, 'article')
			element(null, 'article')
			element(null, 'article')
			element(null, 'article')
			// svg support
			element(null, ['svg', 'http://www.w3.org/2000/svg'], () => {
				element(null, ['a', 'http://www.w3.org/2000/svg'])
			})
			// custom element
			element(null, 'custom-element')
			// cusomized built-in
			element(null, ['form', null, { is: 'custom-form' }])
		})
		element(null, 'footer')
		//
	})
}

/*
<body>
	<header></header>
	<main>
		<article></article>
		<article></article>
		<article></article>
		<article></article>
		<svg>
			<a />
		</svg>
		<custom-element></custom-element>
		<form is='custom-form'></form>
	</main>
	<footer></footer>
</body>
*/

Attributes

Update current rendering element attributes

// Setting single attribute
export declare function attr(name: string, value: OptionalAttrValue, namespaceURI?: string | null): void | null;
// Setting several attributes in one operation
export declare type AttrsMapEntry = AttrValue | [OptionalAttrValue, (string | null)?];
export declare type OptionalAttrMapEntry = AttrsMapEntry | false | null | undefined;
export declare type AttrsMap = Record<string, OptionalAttrMapEntry>;
export declare function attrs<T extends AttrsMap>(inp: T): void;

name: string, the name of attribute .

value: string | number | false | null | undefined, the value of attribute

  • if null | undefined: do nothing
  • else if false: delete attribute
  • else: set attribute to String(value)

namespaceURI: string | null | undefined

Setting several attributes in one operation has corresponding optimizations.

Note: unused attributes will NOT be removed.

Example:

import { render, element, text, attr, attrs } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		element(null, 'form', () => {
			// set all attributes with one operation
			attrs({
				action: '/some/url',
				method: 'post',
				enctype: 'multipart/form-data',
				'delete-attribute': false,
				'skipped-attribute-1': null,
				'skipped-attribute-2': undefined,
			})
			element(null, 'input', () => {
				// make input required
				attr('required', '') // only string, number and "falselike types" allowed
			})
			element(null, 'button', () => {
				text(null, 'submit')
			})
		})
	})
}

/*
<body>
	<form action="/some/url" method="post" enctype="multipart/form-data">
		<input required />
		<button>submit</button>
	</form>
</body>
*/

Text

Create text node with specified key and string or number value.

export declare function text(key: any, value: OptionalTextValue): Text | null;

Key: accepts any type: number, string, symbol or object. Passing null or undefined as key will be replaced with current call order.

Value: accepts string or number. Passing false, null or undefined does not emit text node.

Returns the created text node or null if the node is not created.

Passing NaN as value will emit console warning.

Example:

import { render, element, text } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		element(null, 'header')
		element(null, 'main', () => {
			element(null, 'article', () => {
				text(null, 'Lorem ipsum dolor sit amet.')
			})
			element(null, 'article', () => {
				text(null, 'Lorem ipsum dolor sit amet.')
			})
			element(null, 'article', () => {
				text(null, 'Lorem ipsum dolor sit amet.')
			})
			element(null, 'article', () => {
				text(null, 'Lorem ipsum dolor sit amet.')
			})
		})
		element(null, 'footer')
	})
}

/*
<body>
	<header></header>
	<main>
		<article>Lorem ipsum dolor sit amet.</article>
		<article>Lorem ipsum dolor sit amet.</article>
		<article>Lorem ipsum dolor sit amet.</article>
		<article>Lorem ipsum dolor sit amet.</article>
	</main>
	<footer></footer>
</body>
*/

Normalization of text nodes is not supported! Use string interpolation.

import { render, text } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		// SSR invalid
		const t1 = text(null, 'Lorem ipsum dolor sit amet,')
		const t2 = text(null, ' consectetur adipiscing elit. Vivamus ac.')
		console.log(t1 !== t2) // true
		console.log(t1.textContent === 'Lorem ipsum dolor sit amet,') // true
		console.log(t2.textContent === ' consectetur adipiscing elit. Vivamus ac.') // true
	})
}

/*
<body>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ac.</body>
*/

Comment

Create comment node with specified key and string or number value.

export declare function comment(key: any, value: OptionalCommentValue): Comment | null | undefined;

Key: accepts any type: number, string, symbol or object. Passing null or undefined as key will be replaced with current call order.

Value: accepts string or number. Passing false, null or undefined does not emit comment node.

Returns the created comment node or null if the node is not created.

Passing NaN as value will emit console warning.

Example:

import { render, comment } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		// <!-- creates comment node --!>
		comment(null, 'creates comment node')
	})
}

Lifecycle hooks

All lifecycle hooks work in client side rendering, after render stage.

Created

Called after new element created or used existing sililar element (with same tag name and namespase URI).

export declare function created<T extends Element>(elementCreatedCallback: ElementCreatedCallback<T>): void;

Returned function used as elementRemovedCallback.

Example:

import { render, element, created } from '@st-lib/render'
window.onload = () => {
	render(document.body, () => {
		element(null, 'div', divElement => {
			created(createdElement => {
				console.log('element "div" created', createdElement)
				return removedElement => {
					console.log('element "div" removed', removedElement, divElement === createdElement && divElement === removedElement /* true */)
				}
			})
		})
	})
}

Updated

Called when an element is rendered again.

export declare function updated<T extends Element>(elementUpdatedCallback: ElementUpdatedCallback<T>): void;

Returned function used as elementCleanupCallback

Example:

import { render, element, updated } from '@st-lib/render'

function App() {
	element(null, 'div', divElement => {
		updated(updatedElement => {
			console.log('element has beed updated', updatedElement)
			return cleanupElement => {
				console.log('cleanup', cleanupElement, divElement === updatedElement && divElement === cleanupElement /* true */)
			}
		})
	})
}

window.onload = () => {
	// no console output
	render(document.body, App)
	setTimeout(() => {
		// emits console output
		render(document.body, App)
	})
}

Removed

Called before an element will be removed from document. See created

export declare function removed<T extends Element>(elementRemovedCallback: ElementRemovedCallback<T>): void;

Example:

import { render, element, removed } from '@st-lib/render'

function elementRemovedCallback() {
	console.log('removed')
}

window.onload = () => {
	render(document.body, () => {
		element(0, 'div', divElement => {
			removed(elementRemovedCallback) // same as created(() => elementRemovedCallback)
		})
		element(0, 'span') // replace element <0.div> with <0.span>
	})
}
import { render, element, created, removed } from '@st-lib/render'

function elementRemovedCallback() {
	console.log('will be called once')
}

window.onload = () => {
	render(document.body, () => {
		element(null, 'div', divElement => {
			created(() => elementRemovedCallback)
			removed(elementRemovedCallback)
		})
	})
}

Render stage hooks

Linking

Called instantly during rendering.

Returned function used as elementCleanupCallback.

export declare function linking<T extends Element>(elementReferenceCallback: ElementLinkCallback<T>): void;

Example:

import { render, element, linking } from '@st-lib/render'

window.onload = () => {
	render(document.body, () => {
		element(null, 'button', () => {
			linking(buttonElement => {
				function onClickListener(e) {
					console.log('click', e)
				}
				buttonElement.addEventListener('click', onClickListener, true)
				return () => {
					buttonElement.removeEventListener('click', onClickListener, true)
				}
			})
		})
	})
}

Cleanup

Called every time before rendering. See linking or update.

export declare function cleanup<T extends Element>(elementCleanupCallback: ElementCleanupCallback<T>): void;

SSR (Server Side Rendering)

Use write function for render to string.

export declare function write(content: (ref: null) => void): string;

Example:

// App.js
import { element, text } from '@st-lib/render'

function onClickListener(e) {
	console.log('click', e)
}

export default function App() {
	element(null, 'header')
	element(null, 'main', () => {
		element(null, 'article', () => {
			text(null, 'Lorem ipsum dolor sit amet.')
		})
		element(null, 'article', () => {
			text(null, 'Lorem ipsum dolor sit amet.')
		})
		element(null, 'article', () => {
			text(null, 'Lorem ipsum dolor sit amet.')
		})
		element(null, 'article', () => {
			text(null, 'Lorem ipsum dolor sit amet.')
		})
		element(null, 'button', () => {
			linking(btn => {
				btn.addEventListener('click', onClickListener, true)
				return () => btn.removeEventListener('click', onClickListener, true)
			})
		})
	})
	element(null, 'footer')
}
// client.js
import { render } from '@st-lib/render'
import App from './App'

window.onload = () => {
	render(document.body, App)
}
// server.js
import { write } from '@st-lib/render'
import { createServer } from 'http'
import App from './App'

const server = createServer((_, res) => {
	res.setHeader('Content-Type', 'text/html')
	res.write(`
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>@st-lib/render SSR example</title>
</head>
<body>${write(App)}<script src="/client.js"></script></body>
</html>`)
	res.end()
})

server.listen(3000)