npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-dynamic-portal

v0.1.3

Published

A React library for dynamic component rendering using Redux Toolkit.

Readme

React Dynamic Portal

GitHub npm version npm downloads License

A React library for dynamic component rendering using Redux Toolkit. This library provides a robust and scalable solution for managing and rendering React components dynamically, making it ideal for scenarios like modals, notifications, pop-ups, or any UI element that needs to be injected into the DOM at runtime based on application state.

Table of Contents

Features

This library leverages Redux Toolkit to provide a centralized state management solution for dynamic components, offering the following key features:

  • Centralized State Management: All dynamic components' states (active/inactive, props, notifications) are managed within a Redux store, ensuring a single source of truth and predictable state changes.
  • Dynamic Activation/Deactivation: Components can be activated or deactivated programmatically from any part of your application, triggering their rendering or unmounting.
  • Props Injection: Custom props can be passed to dynamic components upon activation, allowing for flexible and context-aware rendering.
  • Component-Specific Notifications: A built-in notification system allows dynamic components to receive and manage notifications, enabling complex interactions and communication patterns.
  • Type-Safe Development: Written in TypeScript, providing strong typing for enhanced developer experience and reduced errors.
  • Easy Integration: Designed for straightforward integration into existing React and Redux applications.
  • Rollup Bundling: Optimized for production with Rollup, generating CommonJS and ES module formats, along with TypeScript declaration files.

Installation

To install the library, use npm or yarn:

npm install react-dynamic-portal @reduxjs/toolkit react-redux
# or
yarn add react-dynamic-portal @reduxjs/toolkit react-redux

Peer Dependencies: Ensure you have react and react-dom installed in your project, as they are peer dependencies.

npm install react react-dom
# or
yarn add react react-dom

Usage

Integrating with Redux Store

First, you need to add the dynamicComponentReducer to your Redux store.

// src/app/store.js (Example from the `example` directory)
import { configureStore } from '@reduxjs/toolkit';
import { dynamicComponentReducer } from 'react-dynamic-portal';

export const store = configureStore({
  reducer: {
    dynamicComponent: dynamicComponentReducer,
    // ... other reducers
  },
});

DynamicComponentProvider

Wrap your application or the relevant part of your component tree with the DynamicComponentProvider. This provider is responsible for rendering the dynamic components based on their state in the Redux store.

You need to pass an array of components to the provider. Each object in the array should have a unique componentId and the component (a React element) itself.

// src/App.js (Example from the `example` directory)
import React, { useMemo } from 'react';
import {
  DynamicComponentProvider,
  useDynamicComponent,
} from 'react-dynamic-portal';
import Modal from './components/Modal'; // Your dynamic component

function App() {
  const { activate, deactivate, isActive, notifications } =
    useDynamicComponent('myModal');

  const dynamicComponents = useMemo(
    () => [
      {
        componentId: 'myModal',
        component: (
          <Modal
            title='My Dynamic Modal'
            message='This is a dynamically rendered modal!'
          />
        ),
      },
      // Add more dynamic components here
    ],
    []
  );

  return (
    <DynamicComponentProvider components={dynamicComponents}>
      {/* Your application content */}
      <div style={{ textAlign: 'center', marginTop: '50px' }}>
        <h1>React Dynamic Portal Example</h1>
        <button onClick={() => activate({ fromApp: true })} disabled={isActive}>
          Show Modal
        </button>
        <button
          onClick={deactivate}
          disabled={!isActive}
          style={{ marginLeft: '10px' }}
        >
          Hide Modal
        </button>
      </div>
    </DynamicComponentProvider>
  );
  // ...
}

useDynamicComponent Hook

The useDynamicComponent hook allows you to interact with a specific dynamic component by its componentId. It provides methods to activate, deactivate, notify, removeNotification, and access its isActive status and notifications.

// src/App.js (Example from the `example` directory)
import { useDynamicComponent } from 'react-dynamic-portal';

function MyComponent() {
  const { activate, deactivate, isActive, notifications } =
    useDynamicComponent('myModal');

  // ... use activate, deactivate, isActive, notifications
}

Component Props Injection

When a dynamic component is activated using activate(props), the props object passed to activate will be injected into the dynamic component. Additionally, the close and notify functions are automatically injected.

// src/components/Modal.js (Example from the `example` directory)
import React from 'react';

const Modal = ({ title, message, close, notify, fromApp }) => {
  return (
    <div
      style={{
        position: 'fixed',
        top: '50%',
        left: '50%',
        transform: 'translate(-50%, -50%)',
        backgroundColor: 'white',
        padding: '20px',
        border: '1px solid black',
        zIndex: 1000,
      }}
    >
      <h2>{title}</h2>
      <p>{message}</p>
      {fromApp && <p>fromApp</p>} {/* Example of injected prop */}
      <button onClick={close}>Close Modal</button> {/* Injected close function */}
      <button
        onClick={() => notify('modalAction', 'User clicked something in modal')} // Injected notify function
      >
        Notify
      </button>
    </div>
  );
};

export default Modal;
  • close(): A function to deactivate (close) the current dynamic component.
  • notify(notificationId: string | number, payload: any): A function to send a notification to the dynamic component's state.

Notifications System

The useDynamicComponent hook also exposes a notifications array, which contains any notifications sent to that specific component. This allows for a decoupled way for components to receive updates or trigger actions based on external events.

// src/App.js (Example from the `example` directory)
import React, { useEffect } from 'react';
import { useDynamicComponent } from 'react-dynamic-portal';

function App() {
  const { notifications, removeNotification } = useDynamicComponent('myModal');

  useEffect(() => {
    // Process notifications received from the dynamic component
    notifications.forEach(notification => {
      if (notification.id === 'modalAction') {
        console.log('Modal action received:', notification.payload);
        // Perform actions based on the notification
        // For example, show a global toast, log analytics, etc.

        // Important: Remove the notification after processing to avoid re-processing
        removeNotification(notification.id);
      }
    });
  }, [notifications, removeNotification]);

  // ... rest of your component
}

Managing Multiple Dynamic Components

You can manage multiple dynamic components by defining them in the components array of the DynamicComponentProvider and interacting with each using its unique componentId.

// In your main App.js or a parent component
import React, { useMemo } from 'react';
import { DynamicComponentProvider, useDynamicComponent } from 'react-dynamic-portal';
import Modal from './components/Modal';
import Sidebar from './components/Sidebar'; // Another dynamic component
import Toast from './components/Toast';     // Yet another dynamic component

function App() {
  const { activate: activateModal, deactivate: deactivateModal, isActive: isModalActive } = useDynamicComponent('myModal');
  const { activate: activateSidebar, deactivate: deactivateSidebar, isActive: isSidebarActive } = useDynamicComponent('mySidebar');
  const { activate: activateToast, deactivate: deactivateToast, isActive: isToastActive } = useDynamicComponent('myToast');

  const dynamicComponents = useMemo(
    () => [
      {
        componentId: 'myModal',
        component: <Modal title='Main Modal' message='This is the main modal.' />,
      },
      {
        componentId: 'mySidebar',
        component: <Sidebar title='App Sidebar' />,
      },
      {
        componentId: 'myToast',
        component: <Toast message='Operation successful!' />,
      },
    ],
    []
  );

  return (
    <DynamicComponentProvider components={dynamicComponents}>
      {/* ... your application content ... */}
      <button onClick={() => activateModal()} disabled={isModalActive}>Show Main Modal</button>
      <button onClick={() => deactivateModal()} disabled={!isModalActive}>Hide Main Modal</button>

      <button onClick={() => activateSidebar()} disabled={isSidebarActive}>Show Sidebar</button>
      <button onClick={() => deactivateSidebar()} disabled={!isSidebarActive}>Hide Sidebar</button>

      <button onClick={() => activateToast({ duration: 3000 })}>Show Toast</button>
    </DynamicComponentProvider>
  );
}

Customizing Component Styles

Styling of your dynamic components is entirely up to you. The react-dynamic-portal library only handles the rendering and state management. You can use any CSS-in-JS library, CSS modules, or plain CSS.

For example, to style a modal, you would define the styles within your Modal component:

// src/components/Modal.js
import React from 'react';

const Modal = ({ title, message, close }) => {
  const modalStyle = {
    position: 'fixed',
    top: '50%',
    left: '50%',
    transform: 'translate(-50%, -50%)',
    backgroundColor: 'white',
    padding: '20px',
    border: '1px solid #ccc',
    boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
    borderRadius: '8px',
    zIndex: 1000,
  };

  const overlayStyle = {
    position: 'fixed',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0, 0, 0, 0.5)',
    zIndex: 999,
  };

  return (
    <div style={overlayStyle}>
      <div style={modalStyle}>
        <h2>{title}</h2>
        <p>{message}</p>
        <button onClick={close}>Close</button>
      </div>
    </div>
  );
};

export default Modal;

Removing a Dynamic Component

To remove a dynamic component from the DOM, simply call the deactivate() method provided by the useDynamicComponent hook for that specific componentId.

// In any component that uses the hook
import { useDynamicComponent } from 'react-dynamic-portal';

function MyComponent() {
  const { deactivate } = useDynamicComponent('myModal');

  const handleCloseClick = () => {
    deactivate(); // This will remove the 'myModal' component from the DOM
  };

  return <button onClick={handleCloseClick}>Close Modal</button>;
}

Lazy Loading Dynamic Components with React.lazy

For better performance, especially with larger dynamic components, you can use React.lazy to code-split and load them only when needed. The DynamicComponentProvider is designed to work seamlessly with Suspense.

First, define your lazy-loaded component:

// src/components/LazyLoadedModal.js
import React from 'react';

const LazyLoadedModal = ({ title, message, close }) => {
  return (
    <div style={{ /* your modal styles */ }}>
      <h2>{title}</h2>
      <p>{message}</p>
      <button onClick={close}>Close Lazy Modal</button>
    </div>
  );
};

export default LazyLoadedModal;

Then, use React.lazy and include it in your DynamicComponentProvider:

// In your main App.js or a parent component
import React, { useMemo, lazy, Suspense } from 'react';
import { DynamicComponentProvider, useDynamicComponent } from 'react-dynamic-portal';

// Lazily load your component
const LazyModal = lazy(() => import('./components/LazyLoadedModal'));

function App() {
  const { activate, deactivate, isActive } = useDynamicComponent('lazyModal');

  const dynamicComponents = useMemo(
    () => [
      {
        componentId: 'lazyModal',
        component: <LazyModal title='Lazy Modal' message='Loaded on demand!' />,
      },
    ],
    []
  );

  return (
    <DynamicComponentProvider components={dynamicComponents} fallback={<div>Loading dynamic component...</div>}>
      {/* Your application content */}
      <button onClick={() => activate()} disabled={isActive}>
        Show Lazy Modal
      </button>
      <button onClick={deactivate} disabled={!isActive}>
        Hide Lazy Modal
      </button>
    </DynamicComponentProvider>
  );
}

Error Handling

  • Missing componentId: If you try to activate, deactivate, or interact with a componentId that has not been registered with the DynamicComponentProvider, a console.warn message will be logged, indicating that the component ID was not found. Ensure all componentIds used with useDynamicComponent are correctly defined in the DynamicComponentProvider's components array.

    [dynamicComponentSlice] activateComponent: Component ID "nonExistentModal" not found.
  • Duplicate componentIds: While the library itself won't throw an error for duplicate componentIds in the components array passed to DynamicComponentProvider, it will lead to unpredictable behavior as Redux state updates will only affect the last registered component with that ID. Always ensure componentIds are unique.

Best Practices

  • Unique componentIds: Always use unique and descriptive componentIds for each dynamic component to avoid conflicts and ensure predictable behavior.
  • Memoize components Array: When passing the components array to DynamicComponentProvider, use React.useMemo to prevent unnecessary re-renders of the provider and its children. This is crucial for performance.
  • Keep Dynamic Components Simple: Design your dynamic components (e.g., Modal, Sidebar) to be as stateless as possible. Their state should primarily come from the props injected by activate() or from the Redux store if they need to interact with global application state.
  • Clear Notifications: If your dynamic components use the notification system, consider implementing logic to removeNotification after they have been processed to keep the state clean.
  • Error Boundaries: For critical dynamic components, consider wrapping them in React Error Boundaries to gracefully handle rendering errors within the dynamic component itself, preventing the entire application from crashing.
  • Accessibility: Ensure your dynamic components (especially modals and pop-ups) follow accessibility best practices (e.g., proper ARIA roles, keyboard navigation, focus management).
  • Performance: For very complex dynamic components or a large number of them, consider using React.lazy and Suspense for code splitting to reduce initial bundle size and improve loading times.

Contributing

Contributions are welcome! Please feel free to open issues or submit pull requests.

License

This project is licensed under the MIT License.