logo
  • Guide
  • Config
  • Plugin
  • API
  • Examples
  • Community
  • Modern.js 2.x Docs
  • English
    • 简体中文
    • English
    • Introduction
      Plugin System
      CLI Plugins
      CLI Plugin API
      Life Cycle
      Plugin Migration
      Runtime Plugins
      Plugin API
      Life Cycle
      Plugin Migration
      Official Plugins
      CLI Plugins
      BFF Plugin
      SSG Plugin
      styled-components Plugin
      📝 Edit this page
      Previous pagePlugin MigrationNext pageLife Cycle

      #Plugin API

      Modern.js's Runtime Plugins allow you to extend and modify the behavior of your application during its React code execution. With Runtime Plugins, you can easily perform initialization tasks, implement React Higher-Order Component (HOC) wrapping, and more.

      Info

      Runtime plugins need to be configured via the plugins field in src/modern.runtime.ts.

      #Plugin Structure

      A typical Runtime Plugin looks like this:

      import type { RuntimePlugin } from '@modern-js/runtime';
      
      const myRuntimePlugin = (): RuntimePlugin => ({
        name: 'my-runtime-plugin',
        setup: api => {
          // Use the api to register hooks
          api.onBeforeRender(context => {
            console.log('Before rendering:', context);
          });
      
          api.wrapRoot(App => {
            return props => (
              <MyContextProvider>
                <App {...props} />
              </MyContextProvider>
            );
          });
        },
      });
      
      export default myRuntimePlugin;
      • name: A unique identifier for the plugin.
      • setup: The main logic of the plugin, which receives an api object as a parameter. This api object is used to register hooks.

      #API Overview

      The Runtime Plugin API is primarily divided into the following categories:

      • Information Retrieval: Getting runtime configuration and hook functions.
      • Lifecycle Hooks: Executing custom logic at different stages of the application's rendering process.

      #Information Retrieval

      #api.getRuntimeConfig

      Gets the runtime configuration defined by the user in the modern.runtime.ts file.

      • Usage:
      const config = api.getRuntimeConfig();
      console.log(config.myCustomSetting);
      Warning

      This method returns a copy of the user's configuration. Modifying the returned value will not affect the original configuration.

      #api.getHooks

      Gets the hook functions that can be triggered manually.

      • Type:
      () => {
        onBeforeRender: {
          call: (context: any) => Promise<void>;
        }
        // Other hooks...
      };
      • Usage:
      const hooks = api.getHooks();
      await hooks.onBeforeRender.call(myContext);

      #Lifecycle Hooks

      #api.onBeforeRender

      Executes before the application renders (including both server-side rendering and client-side rendering). You can use this hook to perform data prefetching, modify the rendering context, etc.

      • Type:

        type OnBeforeRenderFn<RuntimeContext> = (
          context: RuntimeContext,
        ) => Promise<void> | void;

        RuntimeContext contains contextual information about the current request, such as the request object, response object, etc.

      • Usage:

        api.onBeforeRender(async context => {
          const data = await fetchData(context.req);
          context.data = data; // Add the data to the context
        });
      Warning
      • This hook executes before every render, so avoid performing long-running operations.
      • You can modify the context object in this hook, and the modified context will be passed to subsequent rendering processes.

      #api.wrapRoot

      Allows you to wrap the application's root component with a custom React component. This is commonly used to add global Providers, layout components, etc.

      • Type:

        type WrapRootFn = (App: React.ComponentType<any>) => React.ComponentType<any>;
      • Usage:

        api.wrapRoot((App) => {
          const AppWrapper = (props) => {
            return (
              <MyGlobalProvider>
                <Layout>
                  <App {...props} /> {/* Make sure to pass props */}
                </Layout>
              </MyGlobalProvider>
            );
          };
          return AppWrapper;
        });

        :::warning

      • It is crucial to pass the props to the original App component, otherwise, the application may not function correctly.

      • The component returned by wrapRoot is recreated on every render, so avoid defining complex logic or state within it.

      :::

      #Advanced Usage

      #Combining Hooks

      You can combine multiple hooks to implement more complex functionality. For example, you can use onBeforeRender to fetch data and then use wrapRoot to pass the data to the entire application via Context:

      import { RuntimePlugin, RuntimeContext } from '@modern-js/runtime';
      import { useContext, createContext } from 'react';
      
      export const ThemeContext = createContext<{ theme: string } | null>(null);
      
      export const themePlugin = (): RuntimePlugin => {
        return {
          name: 'theme-plugin',
          setup: api => {
            api.onBeforeRender(async context => {
              const userPreference = await fetch('/api/user/theme-settings').then(
                res => res.json(),
              );
              context.data = {
                theme: userPreference.theme,
              };
            });
      
            api.wrapRoot(App => {
              return props => {
                const context = useContext(RuntimeContext);
                return (
                  <ThemeContext.Provider value={context.data}>
                    <App {...props} />
                  </ThemeContext.Provider>
                );
              };
            });
          },
        };
      };