@ricsam/react-tunnel
TypeScript icon, indicating that this package has built-in type declarations

0.0.5 • Public • Published

@ricsam/react-tunnel

This package allows you to define where you want components to end up in the react component hierarchy. It is like a managed react portal. This is useful if you are dealing with contexts or different React renderers like react-three-fiber or react-art. The code is quite simple, so instead of installing this package from npm you can just copy the code from index.tsx and use it in your project.

npm i @ricsam/react-tunnel

Usage

import { createTunnel, InTunnel, OutTunnel } from "@ricsam/react-tunnel";

// Create a tunnel instance
const tunnel = createTunnel();

// In your layout component
function Layout() {
  return (
    <div>
      <h1>Header</h1>
      <OutTunnel tunnel={tunnel} /> {/* Content will appear here */}
      <div>Other content</div>
    </div>
  );
}

// Deep in your component tree
function DeepComponent() {
  return (
    <div>
      <h2>Deep component</h2>
      <InTunnel tunnel={tunnel}>
        <div>This content will appear after Header</div>
      </InTunnel>
    </div>
  );
}

Examples

Modal Portal

const modalTunnel = createTunnel();

function App() {
  return (
    <div>
      <OutTunnel tunnel={modalTunnel} />
      <PageContent />
    </div>
  );
}

function Modal({ children, isOpen }) {
  if (!isOpen) return null;
  return (
    <InTunnel tunnel={modalTunnel}>
      <div className="modal">{children}</div>
    </InTunnel>
  );
}

Multiple Targets

const primaryTunnel = createTunnel();
const secondaryTunnel = createTunnel();

function Layout() {
  return (
    <div>
      <nav>
        <OutTunnel tunnel={primaryTunnel} />
      </nav>
      <aside>
        <OutTunnel tunnel={secondaryTunnel} />
      </aside>
    </div>
  );
}

function Feature() {
  return (
    <>
      <InTunnel tunnel={primaryTunnel}>
        <PrimaryNavContent />
      </InTunnel>
      <InTunnel tunnel={secondaryTunnel}>
        <SidebarContent />
      </InTunnel>
    </>
  );
}

Dynamic Tunnels

const TunnelContext = createContext<Tunnel>(createTunnel());
function useTunnel() {
  return useContext(TunnelContext);
}

// Use tunnels in your app
function App() {
  const tunnel = useMemo(createTunnel, []);
  return (
    <TunnelContext.Provider value={tunnel}>
      <OutTunnel tunnel={useTunnel().modal} />
      <Content />
    </TunnelContext.Provider>
  );
}

// Consume tunnel anywhere
function Content() {
  const tunnel = useTunnel();
  return (
    <InTunnel tunnel={tunnel}>
      <div>This appears at OutTunnel</div>
    </InTunnel>
  );
}

Extra utils

useTunnelIsOpen

Hook to detect if any InTunnel is currently rendering content. Useful for conditionally rendering OutTunnel wrappers:

function App() {
  const open = useTunnelIsOpen(tunnel);
  return (
    <>
      {open && (
        <SidebarWrapper>
          <OutTunnel tunnel={tunnel} />
        </SidebarWrapper>
      )}
      <MySidebar />
    </>
  );
}
function MySidebar({ children }) {
  return <InTunnel tunnel={tunnel}>{children}</InTunnel>;
}

API

createTunnel()

Creates a new tunnel instance.

<InTunnel tunnel={tunnel}>

Sends children to the corresponding OutTunnel. Only one InTunnel is allowed per tunnel.

<OutTunnel tunnel={tunnel}>

Renders content from the corresponding InTunnel. Multiple OutTunnels can share the same tunnel.

Restrictions

  • Only one InTunnel is allowed per tunnel instance
  • Content is cleared when InTunnel unmounts
  • OutTunnel must be mounted before InTunnel for content to appear

Package Sidebar

Install

npm i @ricsam/react-tunnel

Weekly Downloads

0

Version

0.0.5

License

none

Unpacked Size

15.3 kB

Total Files

10

Last publish

Collaborators

  • ricsam