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.
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.