Installation
npm install @studiometa/js-toolkitThe package name in this documentation
v4 ships as @studiometa/js-toolkit 4.0. While it is in development it lives in the monorepo as @studiometa/js-toolkit-v4, and the code samples on this site import that name so their types resolve against the package that exists today. Read every @studiometa/js-toolkit-v4 as @studiometa/js-toolkit in your own project.
Importing
The root barrel carries everything a page normally needs:
import { Base, registerComponent } from '@studiometa/js-toolkit-v4';Utilities live on /utils:
import { clamp, damp } from '@studiometa/js-toolkit-v4/utils';Test helpers live on /test:
import { mount, settle } from '@studiometa/js-toolkit-v4/test';One subpath per export
Every public export also has a subpath of its own — 197 of them. The barrel is convenient; the subpath is precise:
import { Base } from '@studiometa/js-toolkit-v4/Base';
import { useScroll } from '@studiometa/js-toolkit-v4/useScroll';
import { clamp } from '@studiometa/js-toolkit-v4/utils/clamp';Which one to reach for depends on how the code is delivered:
- With a bundler, prefer the barrel. Tree-shaking removes what you do not import, and the barrel keeps the import lines short.
- From an ESM CDN, with no build step, prefer the subpath. There is no bundler to shake the graph, so a barrel import downloads the barrel's whole module graph before the first line of your code runs.
The subpath layout is what keeps a dependency contained. morphdom is reachable through swap() only, so a page that never swaps content never downloads it.
No build step
v4 needs no compiler. Every decorator is sugar over a function that works without it, so a page loaded from an ESM CDN keeps the whole framework:
<script type="module">
import { Base } from 'https://esm.sh/@studiometa/js-toolkit@4/Base';
import { registerComponent } from 'https://esm.sh/@studiometa/js-toolkit@4/registerComponent';
class Counter extends Base {
static config = { name: 'Counter', refs: ['output'] };
count = 0;
onClick() {
this.count += 1;
this.$refs.output.textContent = String(this.count);
}
}
registerComponent(Counter);
</script>With a build step
Vite
Nothing to configure for the framework itself. Vite 8 transforms TypeScript with Oxc, which passes decorators through untouched — so if you use the decorators, add a transform that compiles them:
// vite.config.js
import { defineConfig } from 'vite';
import swc from '@rollup/plugin-swc';
export default defineConfig({
plugins: [
{
...swc({ swc: { jsc: { target: 'es2022', transform: { decoratorVersion: '2023-11' } } } }),
// Compile only the files that contain a decorator.
enforce: 'pre',
},
],
});TypeScript
Stage-3 decorators need no flag on TypeScript 5. Turn experimentalDecorators off:
{
"compilerOptions": {
"target": "ES2022",
"module": "Preserve",
"moduleResolution": "bundler",
"experimentalDecorators": false,
"useDefineForClassFields": true,
"strict": true
}
}See TypeScript for how to type a component's refs, options and events.
Autoloading
A page that declares its components in the markup does not have to import them all up front. Map each data-component token to a dynamic import and the registry downloads a chunk when an element needs it:
import { registerManifest } from '@studiometa/js-toolkit';
registerManifest({
Accordion: () => import('./components/Accordion.js'),
Map: { load: () => import('./components/Map.js'), mountStrategy: 'visible' },
});See Autoloading.