An open API service indexing awesome lists of open source software.

https://github.com/collagejs/react

React integration for the CollageJS micro-frontend library
https://github.com/collagejs/react

Last synced: 7 days ago
JSON representation

React integration for the CollageJS micro-frontend library

Awesome Lists containing this project

README

          

# CollageJS Logo @collagejs/react

> React integration for the CollageJS micro-frontend library

[Online Documentation](https://collagejs.dev)

This is the official React package for *CollageJS*. It is used for two complementary tasks:

1. Create `CorePiece` objects from React components.
2. Consume `CorePiece` objects (built with any framework or library) in React projects.

> ⚠️ **AI-GENERATED**
>
> This 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.

## Installation

As a first step, create your Vite + React project:

```bash
npm create vite@latest --template react-ts
# OR:
npm create vite@latest --template react-compiler-ts
```

Now proceed to add these packages to make the project a CollageJS Piece project:

```bash
npm install @collagejs/react @collagejs/vite-css
```

This is the same for React projects that wish to expose a *CollageJS* piec and React projects that wish to consume *CollageJS* pieces.

## Creating React-Powered CorePiece Objects

When building a React-powered *CollageJS* piece (micro-frontend), wrap your root React component with `buildPiece()`.

```tsx
// 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() Options

`buildPiece(Component, options)` supports the following options:

1. `props`: default props merged with runtime props.
2. `preMount`: callback invoked before the React root is created.
3. `postUnmount`: callback invoked after unmounting the React root.
4. `rootOptions`: options forwarded to React's `createRoot(...)`.

Example:

```tsx
import { buildPiece } from "@collagejs/react";
import { App } from "./App";

export const myPieceFactory = () =>
buildPiece(App, {
rootOptions: {
identifierPrefix: "my-piece-",
},
});
```

## Consuming CorePiece Objects in React

Use the `Piece` component to mount any CollageJS `CorePiece` in a React app.

```tsx
import { Piece, piece } from "@collagejs/react";
import { myPieceFactory } from "@my/bare-module-specifier";

export function Host() {
return ;
}
```

Important points:

1. Pass the `CorePiece` through the `piece()` helper.
2. Any other props are forwarded to the mounted piece.
3. `Piece` can mount into light DOM (default) or shadow DOM.

### React Best Practice: Keep Piece Identity Stable

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:

```tsx
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 ;
}
```

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.

### piece() Options

Use the second argument of `piece()` to configure mounting behavior.

```tsx

```

> ⚠️ **IMPORTANT**: While the development server is running, touching the values inside the `piece()` function will cause the page to reload.

The options object accepts:

1. `shadow`: See below.
2. `containerProps`: props forwarded to the host `

` element.

#### Shadow Mounting

The `shadow` option supports:

1. `undefined` or `false`: mount in the host element (light DOM).
2. `true`: mount in `attachShadow({ mode: "open" })`.
3. `ShadowRootInit`: mount in a shadow root with custom options, including `mode: "closed"`.

#### Host Container Styling

The host `

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

` also includes a `data-cjs-piece-host` attribute with one of the following values:

1. `dom` for light DOM mounts.
2. `open` for open shadow-root mounts.
3. `closed` for closed shadow-root mounts.

This is useful for global CSS selectors in host apps, for example:

```css
:where([data-cjs-piece-host="dom"]) {
display: contents;
}
```

Or where the attribute value doesn't matter:

```css
:where([data-cjs-piece-host]) {
display: contents;
}
```

We recommend this display setting whenever possible to minimize the layout effects the DIV container may introduce.

#### Host Container Events

`containerProps` accepts any standard React `

` 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:

```tsx
import { useState, useMemo } from "react";
import { Piece, piece } from "@collagejs/react";

export function Host() {
const [lastEvent, setLastEvent] = useState("none");
const corePiece = useMemo(() => myPieceFactory(), []);

return (
<>
setLastEvent("click"),
onKeyDown: () => setLastEvent("keydown"),
},
})}
/>

Last host event: {lastEvent}


>
);
}
```

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.

## Lifecycle Policy (Single-Use)

This package follows CollageJS lifecycle policy strictly:

1. A `CorePiece` instance must not be remounted after unmount.
2. A mounted `CorePiece` instance must not be mounted again concurrently.
3. A `Piece` instance must not switch to a different `CorePiece` input after initialization.
4. A `Piece` instance must not switch shadow mode after mount.

If any of these rules are violated, the library throws explicit runtime errors.

## Parent-Aware Mounting and Context

*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:

1. Exposing `CollageProvider` and `useCollageContext`.
2. Injecting the parent-aware `mountPiece` into context when using `buildPiece()`.
3. Making `Piece` consume context and prefer the parent-aware `mountPiece` when available.

In most cases, this works automatically when both sides use `@collagejs/react` APIs.

## IntelliSense for CorePiece Props

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:

```ts
import type { CorePiece } from "@collagejs/core";

declare module "@my/bare-module-specifier" {
export function myPieceFactory(): CorePiece<{
extra: string;
data: boolean;
}>;
}
```

## Development

When cloning this repository, install packages and Playwrite browsers:

```bash
npm install
npx playwright install
```

Tests are run with `vitest` in Browser Mode:

```bash
npm run test
```

### Test Projects

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