This is part of my series "My Open-Source Projects", where I walk through the projects I started or helped to grow – with the back story, what they do well, where they struggle, and what's next. Previous parts: AngleSharp, MAGES, Piral, Electron.NET and Netpack.
Today's project is the one that is simultaneously my youngest and the one that comes with the biggest "well, actually" disclaimer: Picard.js. It is a micro frontend orchestrator – a small library that can bring micro frontends to any page, no matter how that page was built.
Before you ask: yes, the name is a Star Trek reference. No, I will not stop making "Make it so" jokes. Engage.
What is Picard.js?
Picard.js is a lightweight, flexible variant of the ideas behind Piral. It combines multiple micro frontend technologies – pilets, Module Federation, Native Federation, and more – behind one small, uniform surface. The important bit: you do not need a Piral instance, and you do not need a Native Federation shell, to use it.
The official tagline is "The Captain for Shipping the Next Generation of Micro Frontends Federation and Beyond". The docs also say something I like a lot: Picard is an extension rather than a replacement for the technologies it works with.
In practice that means:
- You have an existing app – an old PHP site, a static site, an ASP.NET page, a server-rendered thing from 2012 that nobody dares to touch.
- You add one script tag.
- You use custom elements such as
<pi-component>and<pi-slot>to say where micro frontends should show up. - Picard loads them (from whatever format they come in), renders them, and keeps the page alive even if one of them explodes.
That's it. No new build, no new shell, no migration project that needs a steering committee.
How it came to be
If you read the Piral article, you know the deal: Piral is a full framework for micro frontends. App shell, CLI, ecosystem, the works. And very good at it. But a full framework is a full commitment – and not every project can, or wants to, commit.
Over the years the same questions kept coming up:
- "Can I use this in my existing app without turning it into a Piral app?"
- "Can I mix pilets with Module Federation?"
- "Can I just load a micro frontend into a plain HTML page?"
The honest answer was always "sort of, but…". Picard is the "yes" to those questions.
The initial version was outlined at the Micro Frontends Conference in June 2024, where it was revealed in the closing keynote, "Painless Micro Frontend Orchestration with Picard". The pitch back then was: one library to just embed micro frontends – no tooling required, no constraints implied. In your existing web app, in a mobile app, or even in an Electron shell.
The first releases followed quickly in July 2024. And then… things got quiet. Development was paused in 2025 for a while. Life, other projects (hello, Netpack), and the small matter of figuring out what the future of Piral should look like.
That last point is actually the interesting one, because the pause had a purpose.
Picard is the foundation of Piral v2
There is now a clear roadmap for Piral v2, and it is built on top of Picard. The FAQ puts it precisely: Picard is not Piral's successor – it is the base library for Piral v2, effectively replacing piral-base.
So the relationship is:
- Piral = the full framework (shell, tooling, ecosystem, opinions)
- Picard = the engine that loads and renders micro frontends (few opinions, many formats)
The most important difference when you pick one: Piral always requires a micro frontend discovery server (a pattern Piral popularized), while Picard treats it as optional. With Picard you can point at a discovery service, hand it a list, give it a function – or simply reference a micro frontend by URL right in the markup.
The Piral v2 plan also gives us a nice tie-in with the Electron.NET article: the presets in the tracking issue are spa, ssr, islands, electron, tauri and react-native. One foundation, many runtimes.
Getting started
The one-liner
<script src="https://unpkg.com/picard-js"></script>
Drop that into a page (or use jsDelivr, or install it from npm as picard-js) and the custom elements start working. If you have a discovery service, add its URL via the feed attribute on the script tag:
<script src="https://unpkg.com/picard-js" feed="https://feed.example.com/api/v1/pilet"></script>
Rendering a component
The workhorse is <pi-component>. You tell it what to render and where it comes from:
<!-- a Module Federation micro frontend -->
<pi-component
name="product-teaser"
source="https://shop.example.com/mf-manifest.json"
format="module">
</pi-component>
<!-- a Native Federation micro frontend -->
<pi-component
name="checkout-button"
source="https://checkout.example.com/remoteEntry.json"
format="native">
</pi-component>
<!-- a pilet -->
<pi-component
name="newsletter"
source="https://cdn.example.com/newsletter/index.meta.json"
format="pilet">
</pi-component>
Other attributes you will meet along the way include remote-name and remote-type (var or esm) for Module Federation, framework (e.g. single-spa), loading-template-id for a loading placeholder, and client (e.g. visible) to control when it gets activated.
Rendering everything that matches
<pi-slot> is the sibling for the "extension slot" idea you may know from Piral: it renders all components registered under a name – from all loaded micro frontends.
<ul>
<pi-slot name="menu-item" item-template-id="menu-entry"></pi-slot>
</ul>
<template id="menu-entry">
<li><slot></slot></li>
</template>
Teams can add menu entries without anyone touching the host page. (Slots also support ordering via attributes.)
Using it from code
Not everything has to be markup. The client variant is a regular module:
import { initializePicard } from 'picard-js/client';
initializePicard({
feed: 'https://feed.example.com/api/v1/pilet',
});
feed is flexible: a URL string, an array of pilet metadata, a discovery response (Piral Feed Service or the MFE working group schema), a map of Native Federation manifests – or a function returning a promise of any of those.
The generic lifecycle
How does Picard render a component written in React, Vue, Svelte or "something my colleague wrote at 2 am"? Via a small, generic lifecycle:
A component exposes some or all of these. A minimal sketch with React looks like this:
import { createRoot } from 'react-dom/client';
import { Teaser } from './Teaser';
export default {
mount(container, props) {
const root = createRoot(container);
root.render(<Teaser {...props} />);
container.__root = root;
},
update(container, props) {
container.__root.render(<Teaser {...props} />);
},
unmount(container) {
container.__root.unmount();
},
};
If you already have single-spa parcels, there is an adapter for those too (framework="single-spa", client only). So you are not forced to rewrite anything to be "Picard compatible".
Errors stay contained
One thing I care about a lot: a micro frontend that fails to load, or crashes while rendering, must not take the page with it. Picard handles bad references and crashing components gracefully. (For debugging, the Piral Inspector works with Picard, too.)
Server-side rendering and islands
Because Picard is "just" a library with a Node variant, it is not restricted to client-side pages.
- The
nodevariant (picard-js/node) takes your HTML, finds thepi-componentelements, and replaces them with their server-rendered output (via thestringifystep of the lifecycle) plus the state the browser needs. There is also thepi-partelement – a server-only replacement rule – which, for example, injects thedisplay: contentsstyling for the custom elements. - The
islandsvariant is a lightweight resumption approach: the page already looks right, the interactive script loads after first paint, and components hydrate only where needed (hydrateattribute, or detected inmountby a non-empty container).
The repository's examples cover static pages (with and without a feed, with routing, with single-spa), an SSR example, an islands example, and dependency sharing. And on the server examples we keep a self-imposed rule: they have to reach a Lighthouse score of 100. It's a good constraint to have when your whole purpose is being lightweight.
Where Picard shines
- Brownfield-friendly. One script tag and some custom elements. Your PHP page doesn't need to know what a bundler is.
- Format-agnostic. Pilets, Module Federation (v1 and v2), Native Federation and plain native ES modules – side by side, on the same page.
- Small. The upcoming 0.3.0 reduces the browser script from 106 kB to 33 kB (the Module Federation runtime is loaded on demand), and the interactive script loads after the first paint.
- Optional discovery. Works great with a discovery server, doesn't require one.
- Resilient. Broken micro frontends don't break the page.
- Framework-agnostic. If it can map to the lifecycle, it can be rendered.
Where it struggles (the honest part)
Let's be adults about this:
- It is an early preview. The changelog says "Early preview: Not suitable for production yet" on every entry. I mean that.
-
The published version is old. The latest release on npm is 0.2.3 from July 2024. The docs already describe 0.3.0, which is still unreleased and lives in
develop. (Yes, the docs are ahead of the package. I know.) - The docs are thin in places. The Electron page is a placeholder, and the FAQ still has "feature complete by end of July 2024" and "production ready in September 2024". Those dates are… let's call them aspirational. They are on my list for the finalization.
- Small community. ~49 GitHub stars and a few dozen weekly downloads. Picard is not widely adopted – yet. If you need something battle-tested today, use Piral.
- Not a framework. There is no CLI that scaffolds your world. That's by design, but it also means more is on you.
Community and licensing
The code is MIT licensed. Questions and discussions happen in the Piral community Discord, and issues and PRs on GitHub are very welcome – especially for the examples and docs.
Contributing is lighter than it sounds: the project uses esbuild for the examples, Playwright for tests (Node 22+), and preview releases (-pre.N) are published straight from develop.
What's next
The roadmap is refreshingly short:
- Finalize Picard v1. Settle the API, finish the docs, remove the "early preview" banner. It will ship toward the end of this year (2026).
- Piral v2 on top of it, with the presets spa, ssr, islands, electron, tauri and react-native.
- More examples and guides – and more FAQ items.
If you have a use case that Picard should handle, please open an issue. This is the right moment to shape it.
Links
- 🌐 Website: picard.js.org
- 📖 Docs: picard.js.org/guide
- 💻 GitHub: picardjs/picard
- 📦 npm: picard-js
Previous articles in this series:
Next up in this series
There are still a few projects waiting in the hangar. Stay tuned – and until then: make it so.










Top comments (0)