WU / DOCSUNIVERSAL MICROFRONTENDS
EN

An editor that keeps its state

Try windows and PiP, drag-and-drop, transitions and keep-alive with two real microapps.

Open Apps in motion. React keeps the editor draft and Vue maintains an inbox of received notes. WU mounts both apps in the same document with separate Shadow DOM roots and coordinates their interaction. The example uses WU Framework 4.0.0 bundled into the build.

Four tasks, one scenario

Try What to observe
Write and send a note Drag the card to Vue or use the send button. The inbox receives a copy of the data after type and permission checks.
Hide and show the editor The draft and React instance remain. No new mount runs.
Change the layout The transition changes the layout; writing and receiving notes keep working.
Detach and return the editor Open a window, write there and return. The same nodes return to the host. PiP is offered when the browser exposes that API.

What each app owns

The shell owns a new WuCore() instance, registers both apps and grants permissions. React owns the draft and offers the docs.note type; Vue validates and receives that type. Visual controls are Lit Custom Elements. The context.wu context preserves each microapp authority when sending or accepting data.

The following snippets run after initializing the runtime and mounting the apps. runtime is the shell instance. To reproduce the complete startup, see the sources at the end of this guide.

Windows and Picture-in-Picture

// Prepare the optional subsystem before the user clicks.
await runtime.windowsReady();

openButton.addEventListener('click', async () => {
  try {
    await runtime.detach('interaction-editor', {
      window: 'popup', width: 560, height: 520,
    });
    status.textContent = String(runtime.isDetached('interaction-editor'));
  } catch (error) {
    status.textContent = error.message;
  }
});

returnButton.addEventListener('click', () => {
  runtime.attach('interaction-editor');
});

Create openButton, returnButton and status before the snippet. For PiP, use window: "pip" from a click after checking support. windowOf(name) reads the current window. Closing the window returns the app; navigating away from the scenario unmounts it. The module keeps executing in the original page and the editor retains the same nodes and state.

Typed delivery with mouse or keyboard

// Sending app: its manifest requests drag.offer:docs.note.
const stopOffer = await context.wu.drag.offer(card, {
  type: 'docs.note',
  payload: () => ({ text: readDraft() }),
});

// The send button uses the same permission and validation path.
await context.wu.drag.deliver('docs.note', { text: readDraft() }, {
  to: 'interaction-board',
});

// Receiving app: its manifest requests drag.accept:docs.note.
const stopAccept = await context.wu.drop.accept(inbox, {
  type: 'docs.note',
  validate(note) {
    if (!note || typeof note.text !== 'string' || !note.text.trim()) {
      throw new TypeError('A non-empty note is required');
    }
    return note;
  },
  onDrop: note => addToInbox(note),
});

This snippet combines two sides: offering and sending belong to the editor; accepting belongs to the inbox. Replace card, inbox, readDraft and addToInbox with each app references. The host must grant both permissions to their respective apps. Keep stopOffer and stopAccept and call them during unmount.

Keep state and change the layout

await runtime.unmount('interaction-editor', { keepAlive: true });
await runtime.show('interaction-editor');

// panelA and panelB have stable data-wu-vt keys inside workspace.
const animation = runtime.transition(() => {
  workspace.classList.toggle('stacked');
}, { root: workspace, duration: 320 });
await animation.updateCallbackDone;
await animation.finished;

Define the stacked class to arrange your panels in one column and add data-wu-vt to the elements WU will measure. Hiding retains the instance; it does not automatically stop intervals or connections you created. The transition respects reduced motion. To remove the whole scenario, call runtime.destroy() and release the host external resources.

Sources and references

The complete example in the official repository includes the manifests and build instructions. Its Lit controls belong to the same project. Clone the complete repository and follow its README to run it; the three downloads below show the integrations and controller.

Read the shell controller, React editor and Vue inbox. These are the sources used to build the example. See drag-and-drop, windows and focus and lifecycle for signatures, permissions and errors.

PiP support depends on the browser; the Chrome documentation describes the user gesture and window lifecycle. The send button lets readers complete the task without dragging, following the WCAG dragging movements criterion.

BUILD WU WITH US

Found a bug? Have an idea?

Your experience improves WU. Tell us what went wrong or help support its development.