📖Browse Tutorials▼

React 18 & 19 (Portals)

React 19

In modern React (v18 & v19), the most robust and idiomatic pattern for multi-window docking is using

React Portals (createPortal)

. This preserves internal component state, React Context, and event bubbling without unmounting or remounting components when panels are dragged between stacks or popped out into auxiliary windows.

1. Installation

Install Strelit UI Kit from npm:

npm install strelit-ui-kit

2. The React Portal Docking Pattern

Instead of letting the layout engine imperatively manage DOM lifecycle, we register a generic component factory that hands container DOM elements over to React. React then projects your virtual DOM components into those containers seamlessly:

import React, { useState, useRef, useLayoutEffect } from 'react';
import { createPortal } from 'react-dom';
import { StrelitLayout, LayoutConfig, ComponentContainer } from 'strelit-ui-kit';
import 'strelit-ui-kit/dist/css/strelit-base.css';
import 'strelit-ui-kit/dist/css/themes/strelit-dark-theme.css';

interface PortalMount {
  id: string;
  element: HTMLElement;
  component: React.ReactNode;
}

export function DockingWorkspace() {
  const containerRef = useRef<HTMLDivElement>(null);
  const layoutRef = useRef<StrelitLayout | null>(null);
  const [portals, setPortals] = useState<PortalMount[]>([]);

  useLayoutEffect(() => {
    if (!containerRef.current) return;

    const config: LayoutConfig = {
      root: {
        type: 'row',
        content: [
          {
            type: 'component',
            componentType: 'portalHost',
            title: 'Telemetry Inspector',
            componentState: { key: 'telemetry' }
          },
          {
            type: 'component',
            componentType: 'portalHost',
            title: 'Event Log Stream',
            componentState: { key: 'logs' }
          }
        ]
      }
    };

    // 1. Initialize StrelitLayout on the mounting DOM element
    const layout = new StrelitLayout(containerRef.current);

    // 2. Register generic host component that hands container element to React
    layout.registerComponentFactoryFunction('portalHost', (container: ComponentContainer, itemConfig) => {
      const el = container.element;
      const state = (itemConfig.componentState as { key: string }) || { key: 'unknown' };
      const key = state.key;

      const component = key === 'telemetry'
        ? <TelemetryViewer />
        : <LogStreamViewer />;

      setPortals(prev => [...prev, { id: key, element: el, component }]);

      container.on('destroy', () => {
        setPortals(prev => prev.filter(p => p.id !== key));
      });
    });

    // 3. Load layout configuration
    layout.loadLayout(config);
    layoutRef.current = layout;

    const handleResize = () => layout.updateRootSize();
    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
      layout.destroy();
    };
  }, []);

  return (
    <div style={{ width: '100vw', height: '100vh', position: 'relative' }}>
      <div ref={containerRef} style={{ width: '100%', height: '100%' }} />
      {portals.map(p => createPortal(p.component, p.element, p.id))}
    </div>
  );
}

3. Advantages of the React 19 Portal Architecture

  • Preserved State: Form inputs, scroll positions, and local component hooks ( useState, useReducer) are never lost during panel drag, drop, or re-ordering.

  • Context Propagation: All React Context providers (Zustand, Redux, TanStack Query, ThemeContext) remain intact across docked panes.

  • Automatic Cleanup: When a user closes a tab, Strelit triggers container.on(‘destroy’), unmounting the portal cleanly and avoiding memory leaks.

⚡ Looking for multi-monitor Popout Windows in React?

Check out our dedicated Working with Popouts Tutorial to learn how Strelit coordinates state across separate browser windows.

Whether you are building complex multi-screen trading terminals, enterprise data workspaces, or migrating from legacy layout engines, our team and community are here to help.

Open an Issue on GitHub →