Chapter 3. Custom plugins in Red Hat Developer Hub


You can integrate custom dynamic plugins into Red Hat Developer Hub to enhance its functionality without modifying its source code or rebuilding it. To add these plugins, export them as derived packages.

While exporting the plugin package, you must ensure that dependencies are correctly bundled or marked as shared, depending on their relationship to the Developer Hub environment.

To integrate a custom plugin into Developer Hub:

  1. First, obtain the plugin’s source code.
  2. Export the plugin as a dynamic plugin package.
  3. Package and publish the dynamic plugin.
  4. Install the plugin in the Developer Hub environment.

To use plugins in Red Hat Developer Hub, you can export plugins as derived dynamic plugin packages. These packages contain the plugin code and dependencies, ready for dynamic plugin integration into Developer Hub.

Prerequisites

  • You have installed the @red-hat-developer-hub/cli package. Use the latest version (@latest tag) for compatibility with the most recent features and fixes.

    Note

    Use the npx @red-hat-developer-hub/cli@latest plugin export command to export an existing custom plugin as a derived dynamic plugin package.

    You must use this command when you have the source code for a custom plugin and want to integrate it into RHDH as a dynamic plugin.

    The command processes the plugin’s source code and dependencies and generates the necessary output for dynamic loading by RHDH.

    For an example of using this command, see Example of installing a custom plugin in Red Hat Developer Hub.

  • You have installed and configured Node.js and NPM.
  • The custom plugin is compatible with your Red Hat Developer Hub version. For more information, see Version compatibility matrix.
  • The custom plugin must have a valid package.json file in its root directory, containing all required metadata and dependencies.

    Backend plugins

    To ensure compatibility with the dynamic plugin support and enable their use as dynamic plugins, existing backend plugins must be compatible with the new Backstage backend system. Additionally, these plugins must be rebuilt using a dedicated CLI command.

    You must export the new Backstage backend system entry point (created using createBackendPlugin() or createBackendModule()) as the default export from either the main package or an alpha package. Export as an alpha package if the plugin instance support still uses alpha APIs. This does not add any additional requirement on top of the standard plugin development guidelines of the plugin instance.

    The dynamic export mechanism identifies private dependencies and sets the bundleDependencies field in the package.json file. This export mechanism ensures that you publish the dynamic plugin package as a self-contained package, with its private dependencies bundled in a private node_modules folder.

    Certain plugin dependencies require specific handling in the derived packages, such as:

    • Shared dependencies: The RHDH application provides these dependencies and lists them as peerDependencies in the package.json file. The dynamic plugin package does not bundle shared dependencies. For example, by default, all @backstage scoped packages use sharing.

      You can use the --shared-package flag to specify shared dependencies that Red Hat Developer Hub application provides and that the dynamic plugin package does not bundle.

      To treat a @backstage package as private, use the negation prefix (!). For example, when a plugin depends on the package in @backstage that is not provided by the Red Hat Developer Hub application.

    • Embedded dependencies: The dynamic plugin package bundles these dependencies with their dependencies hoisted to the top level. By default, the package embeds packages with -node or -common suffixes.

      You can use the --embed-package flag to specify additional embedded packages. For example, packages from the same workspace that do not follow the default naming convention.

      The following is an example of exporting a dynamic plugin with shared and embedded packages:

      $ npx @red-hat-developer-hub/cli@latest plugin export --shared-package '!/@backstage/plugin-notifications/' --embed-package @backstage/plugin-notifications-backend

      In the earlier example:

    • The export treats the @backstage/plugin-notifications package as a private dependency and bundles it in the dynamic plugin package, despite being in the @backstage scope.
    • The export marks the @backstage/plugin-notifications-backend package as an embedded dependency and bundles it in the dynamic plugin package.
    Front-end plugins

    Front-end plugins can use scalprum for configuration. The CLI can generate this configuration automatically during the export process. When running the following command, the CLI logs the generated default configuration:

    $ npx @red-hat-developer-hub/cli@latest plugin export

    The following is an example of default scalprum configuration:

    "scalprum": {
      "name": "<package_name>",  // The Webpack container name matches the NPM package name, with "@" replaced by "." and "/" removed.
      "exposedModules": {
        "PluginRoot": "./src/index.ts"  // The default module name is "PluginRoot" and doesn't need explicit specification in the app-config.yaml file.
      }
    }

    You can add a scalprum section to the package.json file. For example:

    "scalprum": {
      "name": "custom-package-name",
      "exposedModules": {
        "BazModuleName": "./src/baz.ts",
        "QuxModuleName": "./src/qux.ts"
        // Define multiple modules here, with each exposed as a separate entry point in the Webpack container.
      }
    }

    Dynamic plugins might need adjustments for Developer Hub needs, such as static JSX for mountpoints or dynamic routes. These changes are optional but might be incompatible with static plugins.

    To include static JSX, define an additional export and use it as the dynamic plugin’s importName. For example:

    // For a static plugin
    $ export const EntityTechdocsContent = () => {...}
    
    // For a dynamic plugin
    $ export const DynamicEntityTechdocsContent = {
      element: EntityTechdocsContent,
      staticJSXContent: (
        <TechDocsAddons>
          <ReportIssue />
        </TechDocsAddons>
      ),
    };

Procedure

  • Use the plugin export command from the @red-hat-developer-hub/cli package to export the plugin:

    $ npx @red-hat-developer-hub/cli@latest plugin export

    Ensure that you run the earlier command in the root directory of the plugin’s JavaScript package (containing package.json file).

    The dist-dynamic subdirectory has the resulting derived package. The exported package name consists of the original plugin name with -dynamic appended.

    Warning

    Do not publish the derived dynamic plugin JavaScript packages to the public NPM registry. For more appropriate packaging options, see Section 3.2, “Package and publish custom plugins as dynamic plugins”. If you must publish to the NPM registry, use a private registry.

After exporting a custom plugin, you can package the derived package into one of the following supported formats:

  • Open Container Initiative (OCI) image (recommended)
  • TGZ file
  • JavaScript package

    Important

    You must publish exported dynamic plugin packages only to private NPM registries.

3.2.1. Create an OCI image with dynamic packages

Use this procedure to package a dynamic plugin as an OCI image and push it to a container registry.

Prerequisites

  • You have installed podman or docker.

Procedure

  1. Navigate to the plugin’s root directory (not the dist-dynamic directory).
  2. Run the following command to package the plugin into an OCI image:

    $ npx @red-hat-developer-hub/cli@latest plugin package --tag quay.io/example/image:v0.0.1

    In the earlier command, the --tag argument specifies the image name and tag.

  3. Run one of the following commands to push the image to a registry:

    $ podman push quay.io/example/image:v0.0.1
    $ docker push quay.io/example/image:v0.0.1

    The output of the package-dynamic-plugins command provides the plugin’s path for use in the dynamic-plugin-config.yaml file.

3.2.2. Create a TGZ file with dynamic packages

Use this procedure to package a dynamic plugin as a TGZ file and host it on a web server.

Prerequisites

package-and-publish-plugins-as-dynamic-plugins

Procedure

  1. Navigate to the dist-dynamic directory.
  2. Run the following command to create a tgz archive:

    $ npm pack

    You can obtain the integrity hash from the output of the npm pack command by using the --json flag as follows:

    $ npm pack --json | head -n 10
  3. Host the archive on a web server accessible to your RHDH instance, and reference its URL in the dynamic-plugin-config.yaml file as follows:

    plugins:
      - package: https://example.com/backstage-plugin-myplugin-1.0.0.tgz
        integrity: sha512-<hash>
  4. Run the following command to package the plugins:

    $ npm pack --pack-destination ~/test/dynamic-plugins-root/
    Tip

    To create a plugin registry using HTTP server on OpenShift Container Platform, run the following commands:

    $ oc project my-rhdh-project
    $ oc new-build httpd --name=plugin-registry --binary
    $ oc start-build plugin-registry --from-dir=dynamic-plugins-root --wait
    $ oc new-app --image-stream=plugin-registry
  5. Configure your RHDH to use plugins from the HTTP server by editing the dynamic-plugin-config.yaml file:

    plugins:
      - package: http://plugin-registry:8080/backstage-plugin-myplugin-1.9.6.tgz

Use this procedure to publish a dynamic plugin to a private NPM registry.

Warning

Do not publish the derived dynamic plugin JavaScript packages to the public NPM registry. If you must publish to the NPM registry, use a private registry.

Procedure

  1. Navigate to the dist-dynamic directory.
  2. Run the following command to publish the package to your private NPM registry:

    $ npm publish --registry <npm_registry_url>
    Tip

    You can add the following to your package.json file before running the export command:

    {
      "publishConfig": {
        "registry": "<npm_registry_url>"
      }
    }

    If you change publishConfig after exporting the dynamic plugin, re-run the plugin export command to ensure that the correct configuration is in the package.

You can install a custom plugins in Red Hat Developer Hub without rebuilding the RHDH application.

The location of the dynamic-plugin-config.yaml file depends on the deployment method.

The plugins array within the dynamic-plugin-config.yaml file defines plugins. Each object in the array represents a plugin with the following properties:

  • package: The plugin’s package definition, which can be an OCI image, a TGZ file, a JavaScript package, or a directory path.
  • disabled: A boolean value indicating whether you enable or disable the plugin.
  • integrity: The integrity hash of the package, required for TGZ file and JavaScript packages.
  • pluginConfig: The plugin’s configuration. For backend plugins, this is optional; for front-end plugins, you must include it. The pluginConfig is a fragment of the app-config.yaml file, and the system merges any added properties with the RHDH app-config.yaml file.
Note

You can also load dynamic plugins from another directory, though development or testing purposes use this method and it is not recommended for production, except for plugins included in the RHDH container image.

3.3.1. Load a plugin packaged as an OCI image

Use this procedure to load a dynamic plugin from an OCI image into Red Hat Developer Hub.

Prerequisites

Procedure

  1. To retrieve plugins from an authenticated registry, complete the following steps:

    1. Log in to the container image registry.

      podman login <registry>
    2. Verify the content of the auth.json file created after the login.

      cat ${XDG_RUNTIME_DIR:-~/.config}/containers/auth.json
    3. Create a secret file using the following example:

      oc create secret generic _<secret_name>_ --from-file=auth.json=${XDG_RUNTIME_DIR:-~/.config}/containers/auth.json 
      1
      • For an Operator-based deployment, replace <secret_name> with dynamic-plugins-registry-auth.
      • For a Helm-based deployment, replace <secret_name> with <Helm_release_name>_-dynamic-plugins-registry-auth.
  2. Define the plugin with the oci:// prefix by using one of the following formats in your dynamic-plugins.yaml file:

    Standard definition

    Use the format oci://<image_name>:<tag>, as shown in the following example. The installation program automatically extracts the plugin path from the image metadata.

    Example configuration in dynamic-plugins.yaml file:

    plugins:
      - disabled: false
        package: oci://quay.io/example/image:v1.0.0
    Note

    You must package images with the @red-hat-developer-hub/cli to ensure the system applies the io.backstage.dynamic-packages annotation.

    You must define exactly one plugin from that OCI image in the configuration files. The system returns an error if the configuration files contain many plugins or no matching plugins.

    Using image digests

    To perform an integrity check, use the image digest in place of the tag in the dynamic-plugins.yaml file as shown in the following example:

    Example configuration in dynamic-plugins.yaml file:

    plugins:
      - disabled: false
        package: oci://quay.io/example/image@sha256:28036abec4dffc714394e4ee433f16a59493db8017795049c831be41c02eb5dc
    Using version inheritance

    To inherit a version from a base configuration file, for example, dynamic-plugins.default.yaml, use the {{inherit}} placeholder to inherit the v0.0.2 tag, as shown in the following example:

    Example configuration in dynamic-plugins.default.yaml file:

    plugins:
      - disabled: false
        package: oci://quay.io/example/image:v0.0.2

    Example configuration in dynamic-plugins.yaml file:

    includes:
      - dynamic-plugins.default.yaml
    plugins:
      - disabled: false
        package: oci://quay.io/example/image:{{inherit}}
    Note

    An error occurs if you use {{inherit}} in the includes file itself or if no matching plugin key exists in the base configuration.

  3. To apply the changes, restart the RHDH application.

3.3.2. Load a plugin packaged as a TGZ file

Use this procedure to load a dynamic plugin from a TGZ file into Red Hat Developer Hub.

Prerequisites

Procedure

  1. Specify the archive URL and its integrity hash in the dynamic-plugins.yaml file using the following example:

    plugins:
      - disabled: false
        package: https://example.com/backstage-plugin-myplugin-1.0.0.tgz
        integrity: sha512-9WlbgEdadJNeQxdn1973r5E4kNFvnT9GjLD627GWgrhCaxjCmxqdNW08cj+Bf47mwAtZMt1Ttyo+ZhDRDj9PoA==
  2. To apply the changes, restart the RHDH application.

Use this procedure to load a dynamic plugin from a JavaScript package into Red Hat Developer Hub.

Prerequisites

Procedure

  1. Run the following command to obtain the integrity hash from the NPM registry:

    $ npm view --registry <registry_link> <npm_package>@<version> dist.integrity
  2. Specify the package name, version, and its integrity hash in the dynamic-plugins.yaml file as follows:

    plugins:
      - disabled: false
        package: @example/backstage-plugin-myplugin@1.0.0
        integrity: sha512-9WlbgEdadJNeQxdn1973r5E4kNFvnT9GjLD627GWgrhCaxjCmxqdNW08cj+Bf47mwAtZMt1Ttyo+ZhDRDj9PoA==
  3. If you are using a custom NPM registry, create a .npmrc file with the registry URL and authentication details:

    registry=<registry_link>
    //<registry_link>:_authToken=<auth_token>
  4. When using OpenShift Container Platform or Kubernetes:

    1. Use the Helm chart to add the .npmrc file by creating a secret. For example:

      apiVersion: v1
      kind: Secret
      metadata:
        name: <release_name>-dynamic-plugins-npmrc
      type: Opaque
      stringData:
        .npmrc: |
          registry=<registry_link>
          //<registry_link>:_authToken=<auth_token>

      Replace <release_name> with your Helm release name. This name is a unique identifier for each chart installation in the Kubernetes cluster.

    2. For RHDH Helm chart, name the secret using the following format for automatic mounting:

      <release_name>-dynamic-plugins-npmrc

  5. To apply the changes, restart the RHDH application.

This example demonstrates how to package and install dynamic plugins by using the Backstage Entity Feedback community plugin that is not included in Red Hat Developer Hub pre-installed dynamic plugins.

Limitations:

  • You need to ensure that you build your custom plugin with a compatible version of Backstage. In Developer Hub, click Settings. Your custom plugin must be compatible with the Backstage Version (or the closest earlier version) that the Metadata section of Red Hat Developer Hub displays.

    For example, if you view the history of the backstage.json file for the Entity Feedback plugin, the 1fc87de commit is the closest earlier version to Backstage version of 1.39.1.

    backstage.json file history in GitHub

Prerequisites

  • Your local environment meets the following requirements:
  • Node.js: Version 22.x
  • Yarn: Version 4.x
  • git CLI
  • jq CLI: Command-line JSON processor
  • OpenShift CLI (oc): The client for interacting with your OpenShift cluster.
  • Container runtime: You need either podman or docker to package the plugin into an OCI image and to log in to registries.
  • Container registry access: Access to an OCI-compliant container registry, such as the internal OpenShift registry or a public registry such as Quay.io.

Procedure

  1. Clone the source code for the Entity Feedback plugin, as follows:

    $ git clone https://github.com/backstage/community-plugins.git
    $ cd community-plugins
  2. Prepare your environment to build the plugin by enabling Yarn for your Node.js installation, as follows:

    $ corepack enable yarn
  3. Install the dependencies, compile the code, and build the plugins, as follows:

    $ cd workspaces/entity-feedback
    $ yarn install
    $ yarn tsc
    $ yarn build:all
    Note

    After this step, with upstream Backstage, you publish the built plugins to a NPM or NPM-compatible registry. In this example, as you are building this plugin to support dynamic loading by Red Hat Developer Hub, you can skip the npm publish step that publishes the plugin to a NPM registry. Instead, you can package the plugin for dynamic loading and publish it as a container image on Quay.io or your preferred container registry.

  4. Prepare the Entity Feedback front-end plugin by using the Red Hat Developer Hub CLI. The following command uses the plugin files in the dist folder that the yarn build:all command generated, and creates a new dist-scalprum folder that has the necessary configuration and source files to enable dynamic loading:

    $ cd plugins/entity-feedback
    $ npx @red-hat-developer-hub/cli@latest plugin export

    When this command packages a front-end plugin, it uses a default Scalprum configuration if one is not found. The Scalprum configuration specifies the plugin entry point and exports, and then builds a dist-scalprum folder that has the dynamic plugin. The following example shows the default Scalprum configuration. However, you can add a scalprum key to the package.json file used by your plugin to set custom values, if necessary:

    {
      "name": "backstage-community.plugin-entity-feedback",
      "exposedModules": {
        "PluginRoot": "./src/index.ts"
      }
    }

    Red Hat Developer Hub uses the following plugin-manifest.json file to load the plugin. This file is in the dist-dynamic/dist-scalprum folder:

    {
      "name": "backstage-community.plugin-entity-feedback",
      "version": "0.6.0",
      "extensions": [],
      "registrationMethod": "callback",
      "baseURL": "auto",
      "loadScripts": [
        "backstage-community.plugin-entity-feedback.fd691533c03cb52c30ac.js"
      ],
      "buildHash": "fd691533c03cb52c30acbb5a80197c9d"
    }
  5. Package the plugin into a container image and publish it to Quay.io or your preferred container registry:

    $ export QUAY_USER=replace-with-your-username
    $ export PLUGIN_NAME=entity-feedback-plugin
    $ export VERSION=$(cat package.json | jq .version -r)
    
    $ npx @red-hat-developer-hub/cli@latest plugin package \
      --tag quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
    
    $ podman login quay.io
    $ podman push quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
  6. Repeat the same steps for the backend plugin. Backend plugins do not require Scalprum, and the export generates a dist-dynamic folder instead of a dist-scalprum folder:

    $ cd ../entity-feedback-backend/
    $ npx @red-hat-developer-hub/cli@latest plugin export
    
    $ export QUAY_USER=replace-with-your-username
    $ export PLUGIN_NAME=entity-feedback-plugin-backend
    $ export VERSION=$(cat package.json | jq .version -r)
    
    $ npx @red-hat-developer-hub/cli@latest plugin package \
      --tag quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION
    
    $ podman push quay.io/$QUAY_USER/$PLUGIN_NAME:$VERSION

    Those commands publish two container images to your container registry.

    The following image shows the container images published to Quay.io:

    Container images published to Quay.io

Use this procedure to add custom dynamic plugins to Red Hat Developer Hub by updating the dynamic-plugins.yaml configuration file.

Procedure

  • To add your custom dynamic plugins to Red Hat Developer Hub, update the dynamic-plugins.yaml file by using the following configuration that the npx @red-hat-developer-hub/cli@latest plugin package command generates:

    plugins:
      - package: oci://quay.io/_<user_name>_/entity-feedback-plugin:0.5.0
        disabled: false
      - package: oci://quay.io/_<user_name>_/entity-feedback-plugin-backend:0.6.0
        disabled: false
    Note

    Ensure that your container images are publicly accessible, or that you have configured a pull secret in your environment. A pull secret provides Red Hat Developer Hub with credentials to authenticate pulling your plugin container images from a container registry.

3.6. Display the front-end plugin

Use this procedure to configure and display a front-end plugin in the Red Hat Developer Hub UI.

Procedure

  1. Update the pluginConfig section of your dynamic-plugins.yaml file to specify how to add the Entity Feedback to the Red Hat Developer Hub UI.

    dynamic-plugins.yaml file fragment

    - package: oci://quay.io/_<user_name>_/entity-feedback-plugin:0.5.0
      disabled: false
      pluginConfig:
        dynamicPlugins:
          frontend:
            backstage-community.plugin-entity-feedback:
              entityTabs:
                - mountPoint: entity.page.feedback
                  path: /feedback
                  title: Feedback
              mountPoints:
                - config:
                    layout:
                      gridColumn: 1 / -1
                  importName: StarredRatingButtons
                  mountPoint: entity.page.feedback/cards
                - config:
                    layout:
                      gridColumn: 1 / -1
                  importName: EntityFeedbackResponseContent
                  mountPoint: entity.page.feedback/cards
                - config:
                    layout:
                      gridColumnEnd:
                        lg: span 6
                        md: span 6
                        xs: span 6
                  importName: StarredRatingButtons
                  mountPoint: entity.page.overview/cards

    where:

    backstage-community.plugin-entity-feedback:entityTabs
    Enter the entityTabs array to define a new tab, named “Feedback” on the Entity Overview screen in Red Hat Developer Hub.
    frontend:mountPoints
    This array defines the following configurations to mount React components exposed by the plugin:
  2. This configuration adds the StarredRatingButtons component to the new Feedback tab defined in entityTabs.
  3. Similar to the StarredRatingButtons, this configuration mounts the EntityFeedbackResponseContent on the Feedback tab.
  4. This configuration adds the StarredRatingButtons to the default Overview tab for each entity.
  5. To complete installing the Entity Feedback plugins, you must redeploy your Red Hat Developer Hub instance.

Verification

When your new instance of Red Hat Developer Hub has started, you can check that your plugins install and enable by visiting the Administration > Extensions screen and searching for “entity” on the Installed tab.

Custom extensions in the Extensions page

When you click Catalog, you should see the new Feedback tab, and the StarredRatingButtons displayed, as follows:

Entity view showing the Feedback tab and StarredRatingButtons

Selecting a low star rating prompts the user to offer feedback, as follows:

Feedback overlay dialog prompting for user feedback
Note

The system does not save user feedback if you log in as the Guest user.

Red Hat logoGithubredditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust. Explore our recent updates.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

Theme

© 2026 Red Hat
Back to top