How to build several Custom UI components with one react app in static folder

In forge do you always need to create a new react app for each and every Custom UI component you want to build? can you have one react app from multiple Custom UI components?

Hi @OnucheIdoko1, Custom UI will serve the static bundle you specify in the resource path — depending on your build toolchain you could share a lot of components between different Custom UI apps and build different bundles (i.e. using webpack config). I’d be keen to hear about your use case so I can help further!

I ended up using https://github.com/lerna with CRA. Webpack was a bit challenging for me.

1 Like

@nhur I think an example app that combined multiple custom UI elements would be very helpful. For example an app with custom UI’s for Jira:issuePanel and a Jira:isssueGlance. Assuming I build a react component for each custom element what additional webpack configuration would be needed and how would I reference the desired element in the manifest.yml? @OnucheIdoko1 if you can share further your solution that would be great.

Hello @jeffryan, So with Lerna you can manage a monorepo for React apps development. So for each custom-ui component you can do CRA for it and develop as you want, then “npm run build” to get the static files. All your apps will be in a packages dir. So you can have packages/custom-ui-1, packages/custom-ui-2 and packages/custom-ui-3. With this, you can do “npm run build” in each app and get your development build path. It also helps with having a shared folder (e.g. packages/shared), where you can keep all your shared components and reuse them. You can have a look at this post: https://medium.com/@rithikachowta and this repo: https://github.com/biernacki/cra-monorepo

Thank you very much for your response @OnucheIdoko1! My understanding then is that using the monorepo you can easily share components among your different apps and lerna makes managing the monorepo fairly easy.

However it still sounds like you will end up with a full bundle for each application element. For example I would end up with a CRA bundle for Jira:issuePanel and another for Jira:issueGlance and that each bundle may have duplicate code from the other. Is this correct?

Thanks again for you insights!
Jeff

Hello @jeffryan, You are correct. But, in order not to have duplicates, you can keep shared components in the shared folder and import them into each CRA bundle.

1 Like

Today, I shared my similar idea here

2 Likes

As of today (02/04/2021), with the newest version of @forge/bridge, we can easily archive this.
Short description: Just define the same resource for your modules. And render the content of React app based on where the app is running.

Here is my manifest.yml which shared just one resource and resolver for multi modules:

modules:
  jira:adminPage:
    - key: admin-page
      resource: custom-ui-resource
      resolver:
        function: custom-ui-resolver
      title: Admin Page Jira Cloud
  jira:issuePanel:
    - key: issue-panel
      resource: custom-ui-resource
      resolver:
        function: custom-ui-resolver
      title: Issue Panel

This is my top App.js in my React app:

import React, { useEffect, useState } from 'react';
import { view } from '@forge/bridge';

function App() {
  const [context, setContext] = useState({});
  const [isDetectingContext, setDetectingContext] = useState(true);

  useEffect(() => {
    setDetectingContext(true);

    // Use the "view" from Forge Bridge to get the context
    // where this React app is running
    view.getContext()
      .then(setContext)
      .finally(() => setDetectingContext(false));
  }, []);

  if (isDetectingContext) {
    return <div>Detecting context...</div>;
  }

  // Detect the module which calling this React app,
  // and render the content for that
  switch (context.moduleKey) {
    case 'admin-page':
      // Render and "AdminPage" if we are in module "admin-page"
      return <AdminPage />;
    case 'issue-panel':
      // Render and "IssuePanel" if we are in module "issue-panel"
      return <IssuePanel />;
    default:
      return <div>Cannot Detect Context</div>;
  }
}

export default App;

The code and comments tell everything. I hope it help.

9 Likes

This is really great @nhac.tat.nguyen ! I tried it out yesterday and seems to work well. Thanks to you and @clouless and others that pushed things to this point.

2 Likes