ce-autoloader
Load web components on demand
if and when they're needed.
About
The ce-autoloader is a lightweight web Components lazy loader.
- Universal: Anything that exports a Web Component works (React, Vue, Lit, Svelte, etc.).
- Shared Catalog: Define your components in a single place and re-use it across multiple pages.
-
Performance: Use
loadingattribute to customize when a component should be loaded, maximizing network performance with minimal overhead. - Wildcard Loaders: Wildcard loaders allow you to load a whole design system with minimal footprint.
- Import maps: Native support for import-maps allow you to define modules without a compiler/bundler.
-
Polished Animations: Animate components as they load with the
celifecycle states and plain CSS, providing a smooth and polished user experience. - Smart Scheduling: Automatically batches into a single animation frame to prevent layout thrashing.
- Native Telemetry: Built-in performance markers for every stage of the lifecycle, visible in the Performance DevTools.
- Error Handling: Define a custom fallback component to show when a module fails to load.
- Tiny: 3kb gzipped without dependencies!
Checkout the demos below to see how it works, and the documentation for more details on how to use it.
Get started
It's just 3 steps. No build tools required!
npm install ce-autoloader
import CERegistry from 'ce-autoloader'; const registry = new CERegistry({ catalog: { 'my-component': () => import('./my-component.js') } });
<my-component loading="visible">World</my-component>
Documentation
Universal
Built to load any standard Web Component, regardless of framework.
Because ce-autoloader targets the standard Custom Elements API, it doesn't
care how your component was built. You can mix components from React,
Vue, Svelte, Lit, or Vanilla JS in the
same page without any problems.
The example below are three components with different frameworks but sharing the same state with preact signals.
Loading Strategies
Fine-tune component loading for every unique use case.
The loading attribute defines the strategy for when a component is loaded. By
default, components use the visible strategy to maximize initial performance.
Eager
Downloads the module as soon as it's found in the DOM. Use this for critical "above the fold" components like navigation headers or hero sections.
Visible (Default)
Components are downloaded and defined only when they enter the viewport, using a Intersection Observer. Perfect for content "below the fold" to keep initial page weight low.
Click
The module is fetched when a user clicks or touches the element. Ideal for heavy UI widgets like maps, complex editors, or modal content.
Custom
Any custom string (e.g., loading="lazy-load") can be used as a loading strategy, and triggered
manually via
registry.upgrade(document.body, 'lazy-load'). This is useful for custom workflows that
don't fit
into
the built-in strategies.
Custom Loaders
Tailor the loader to your specific workflow.
Don't want to register 250 components one by one? we got you. Wildcard Loaders allow you to customize the loader to fit your needs.
In this example, we registered a loader for
nord-*. The browser sees <nord-qrcode>, and calls our loader, that fetches
the correct module from the CDN.
/** * Custom loader for the Nord Health components */ "nord-*": async (full_name) => { // 1. Extract the specific component name (e.g., 'qrcode' from 'nord-qrcode') const [, namespace, name] = full_name.match(/^([a-z]+)-(.*)/); // 2. Resolve the module URL (dynamic import) const module = await import(/* @vite-ignore */ `https://esm.sh/@nordhealth/components/lib/${capitalize(name)}.js`); // 3. Define the component if not already registered if (!customElements.get(full_name)) { customElements.define(full_name, module.default); } return module },
Now, every time the browser sees a tag with prefix
nord
tag, it will automatically load the component from the esm CDN.
<nord-qrcode>
<nord-select>
<nord-date-picker>
<nord-command-menu>
<nord-tag-group>
Animations & Transitions
Define exactly how and when components surface.
Web components should feel like a native part of the page, not an afterthought. Orchestrate how elements surface with coordinated entry patterns that eliminate layout shift and create a fluid, intentional experience from the first frame.
Every component moves through a predictable state machine that you can style with CSS
:not(:defined) → [ce="loading"] → [ce="loaded"] → [ce="defined"]
Placeholder state :not(:defined)
The custom element is in the DOM but the browser doesn't know its behavior yet. Use this to set min-height or aspect-ratio to reserve space on the page.
:not(:defined) elementLoading state [ce="loading"]
Triggered when the module is being loaded. This is the perfect time to show a Skeleton Loader or a progress indicator.
Interactive state [ce="defined"]
The component is defined and upgraded to a fully interactive web component. defined is
written synchronously in the frame the element upgrades, and it is the last state of the success walk
— so an entrance animation declared on it plays out in full.
Native Telemetry & Insights
With telemetry baked into the core, you get deep visibility into load times and execution overhead using native browser APIs.
Error Fallbacks & Resilience
Define your own fallback component via
fallback, that is displayed when a module throws an error during loading:
You can also attach a function whenever an error occurs by setting hooks.error.
Examples
- CDN + Import-maps Load directly from CDN and configure with import-maps
- Micro-Frontends + Vite Framework agnostic Tractor Store with Vite + Preact store
- A/B Testing A/B Testing webcomponents is very simple!
FAQ (Perguntas Frequentes)
Why web components?
Web components with light-dom&css variables are the GOAT! They are the only way to reuse components across frameworks, and since they're part of HTML spec, they'll still work in 20 years.
Aren't you tired of recreating the same components over and over again every 5 years with the new shiny framework? Let's stop this madness!
⚛️ How to use this with react/vue/svelte/etç?
Vue and Svelte already export web-components out of the box!
Only React is purposefully missing this feature, so you need to use a library like remount or react-to-web-component.
import r2wc from "@r2wc/react-to-web-component" const Greeter = ({ name }) => { return <div>Hello, {name}!</div> } export default r2wc(Greeter, { props: { name: "string", } });
🚀 Wait, isn't this like Astro?
Yes and No. Both use the "Island Architecture" concept to reduce JS bloat, but they are :
- Astro (Server-First): Renders at build time or at server. Great for new apps.
- ce-autoloader (Client-First): Runs at runtime. Great for adding islands to existing Python, Ruby, Elixir or static HTML pages.
🚀 What's the lighthouse performance like?
These are the metrics of the page you're reading now, measured live in your browser: