React integration for the CollageJS micro-frontend library
This is the official React package for CollageJS. It is used for two complementary tasks:
- Create
CorePieceobjects from React components. - Consume
CorePieceobjects (built with any framework or library) in React projects.
⚠️ AI-GENERATEDThis package's main functionality was mainly produced by Github Copilot. Unit testing and test projects say this is working as expected, but if you encounter issues, have this in mind and if you have the expertise, have a look at the source code to help speed up any potential fixes.
As a first step, create your Vite + React project:
npm create vite@latest --template react-ts
# OR:
npm create vite@latest --template react-compiler-tsNow proceed to add these packages to make the project a CollageJS Piece project:
npm install @collagejs/react @collagejs/vite-cssThis is the same for React projects that wish to expose a CollageJS piec and React projects that wish to consume CollageJS pieces.
When building a React-powered CollageJS piece (micro-frontend), wrap your root React component with buildPiece().
// piece.tsx
import { buildPiece } from "@collagejs/react";
import { cssMountFactory } from "@collagejs/vite-css/ex";
import { App } from "./App";
// Only one cssMount per entry file is needed.
// REQUIRED: The string here is the file's name, without extension.
const cssMount = cssMountFactory("piece");
export function myPieceFactory() {
const piece = buildPiece(App /*, { options } */);
return {
// Keep cssMount before piece.mount to prevent FOUC.
mount: [cssMount, piece.mount],
update: piece.update,
};
}buildPiece() is the public helper. It wraps a React component into a
CollageJS CorePiece object and can be customized with lifecycle hooks and default props through its options argument.
buildPiece(Component, options) supports the following options:
props: default props merged with runtime props.preMount: callback invoked before the React root is created.postUnmount: callback invoked after unmounting the React root.rootOptions: options forwarded to React'screateRoot(...).
Example:
import { buildPiece } from "@collagejs/react";
import { App } from "./App";
export const myPieceFactory = () =>
buildPiece(App, {
rootOptions: {
identifierPrefix: "my-piece-",
},
});Use the Piece component to mount any CollageJS CorePiece in a React app.
import { Piece, piece } from "@collagejs/react";
import { myPieceFactory } from "@my/bare-module-specifier";
export function Host() {
return <Piece {...piece(myPieceFactory())} extra="yes" data={true} />;
}Important points:
- Pass the
CorePiecethrough thepiece()helper. - Any other props are forwarded to the mounted piece.
Piececan mount into light DOM (default) or shadow DOM.
In React, avoid creating a new CorePiece object on every render. Because
CollageJS pieces are single-use, repeatedly creating them inline can trigger
unnecessary remounts and lifecycle conflicts.
Prefer memoizing the piece object:
import { useMemo } from "react";
import { Piece, piece } from "@collagejs/react";
import { myPieceFactory } from "@my/bare-module-specifier";
export function Host() {
const corePiece = useMemo(() => myPieceFactory(), []);
return <Piece {...piece(corePiece)} extra="yes" data={true} />;
}This keeps the same piece identity for the lifetime of the component instance. During HMR, React may still replace modules and identities internally, and this package accepts that development-only case.
Use the second argument of piece() to configure mounting behavior.
<Piece
{...piece(myPieceFactory(), {
shadow: true,
containerProps: { className: "host" },
})}
/>
⚠️ IMPORTANT: While the development server is running, touching the values inside thepiece()function will cause the page to reload.
The options object accepts:
shadow: See below.containerProps: props forwarded to the host<div>element.
The shadow option supports:
undefinedorfalse: mount in the host element (light DOM).true: mount inattachShadow({ mode: "open" }).ShadowRootInit: mount in a shadow root with custom options, includingmode: "closed".
The host <div> is intentionally unstyled by this package.
If you want host-level styling, pass it through containerProps, for example
containerProps.style or containerProps.className.
Each host <div> also includes a data-cjs-piece-host attribute with one of the following values:
domfor light DOM mounts.openfor open shadow-root mounts.closedfor closed shadow-root mounts.
This is useful for global CSS selectors in host apps, for example:
:where([data-cjs-piece-host="dom"]) {
display: contents;
}Or where the attribute value doesn't matter:
:where([data-cjs-piece-host]) {
display: contents;
}We recommend this display setting whenever possible to minimize the layout effects the DIV container may introduce.
containerProps accepts any standard React <div> props, including event
handlers. This lets you react to host-level interactions around a mounted
piece without changing the piece implementation.
Use bubbling events (for example onClick, onKeyDown, onInput) so events
from descendants can reach the host container:
import { useState, useMemo } from "react";
import { Piece, piece } from "@collagejs/react";
export function Host() {
const [lastEvent, setLastEvent] = useState("none");
const corePiece = useMemo(() => myPieceFactory(), []);
return (
<>
<Piece
{...piece(corePiece, {
containerProps: {
onClick: () => setLastEvent("click"),
onKeyDown: () => setLastEvent("keydown"),
},
})}
/>
<p>Last host event: {lastEvent}</p>
</>
);
}This pattern works well for listening to interactions that bubble from content
mounted inside the piece. A notable pair of useful, bubbling events: focusin and focusout. These can notify the host application whenever a CollageJS piece has received or lost keyboard focus.
This package follows CollageJS lifecycle policy strictly:
- A
CorePieceinstance must not be remounted after unmount. - A mounted
CorePieceinstance must not be mounted again concurrently. - A
Pieceinstance must not switch to a differentCorePieceinput after initialization. - A
Pieceinstance must not switch shadow mode after mount.
If any of these rules are violated, the library throws explicit runtime errors.
CollageJS supports parent-aware lifecycle management: When a parent piece unmounts, child pieces mounted through that parent-aware mountPiece function are unmounted first.
This package supports that model by:
- Exposing
CollageProvideranduseCollageContext. - Injecting the parent-aware
mountPieceinto context when usingbuildPiece(). - Making
Piececonsume context and prefer the parent-awaremountPiecewhen available.
In most cases, this works automatically when both sides use @collagejs/react APIs.
You can get IntelliSense for props passed to Piece if your factory function's return type is known.
One approach is to declare the module in a .d.ts file:
import type { CorePiece } from "@collagejs/core";
declare module "@my/bare-module-specifier" {
export function myPieceFactory(): CorePiece<{
extra: string;
data: boolean;
}>;
}When cloning this repository, install packages and Playwrite browsers:
npm install
npx playwright installTests are run with vitest in Browser Mode:
npm run testThere are 2 test projects under the test-projects/ folder: One creates a CollageJS piece using buildPiece(); the other one consumes pieces using the Piece component.
Both projects are standard Vite + React projects with the corresponding Vite plug-ins installed according to their roles.