Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use a vanilla JavaScript package in React app

I am aiming to use a Vanilla JavaScript package in a more sophisticated React app to build additional logic around the JavaScript package.

The JavaScript library is LabelStudio and docs can be found here: https://github.com/heartexlabs/label-studio-frontend

However, when I try to import the LabelStudio I get an error saying Module not found: Can't resolve 'label-studio' , as described here https://github.com/heartexlabs/label-studio-frontend/issues/55

Since my understanding of frontend code is limited, I am not sure whether this is something the developers did not expected users to do and just wanted them to use the entire library and customized instead of using the library as a component. My idea was to use the library as in the vanilla javascript example here:

<!-- Include Label Studio stylesheet -->
<link href="https://unpkg.com/[email protected]/build/static/css/main.0a1ce8ac.css" rel="stylesheet">

<!-- Create the Label Studio container -->
<div id="label-studio"></div>

<!-- Include the Label Studio library -->
<script src="https://unpkg.com/[email protected]/build/static/js/main.3ee35cc9.js"></script>

<!-- Initialize Label Studio -->
<script>
  var labelStudio = new LabelStudio('label-studio', {
    config: `
      <View>
        <Image name="img" value="$image"></Image>
        <RectangleLabels name="tag" toName="img">
          <Label value="Hello"></Label>
          <Label value="World"></Label>  
        </RectangleLabels>
      </View>
    `,

    interfaces: [
      "panel",
      "update",
      "controls",
      "side-column",
      "completions:menu",
      "completions:add-new",
      "completions:delete",
      "predictions:menu",
    ],

    user: {
      pk: 1,
      firstName: "James",
      lastName: "Dean"
    },

    task: {
      completions: [],
      predictions: [],
      id: 1,
      data: {
        image: "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg"
      }
    },
    
    onLabelStudioLoad: function(LS) {
      var c = LS.completionStore.addCompletion({
        userGenerate: true
      });
      LS.completionStore.selectCompletion(c.id);
    }
  });
</script>   

How can I make use of the above code in a React Component to facilitate dynamic data loading and use of state to customize the functions?

like image 417
Cactus Avatar asked Sep 02 '25 09:09

Cactus


1 Answers

I don't have a solution making the npm module label-studio to work. I tried importing the dist file instead, but it errors

Expected an assignment or function call and instead saw an expression

So here's a workaround until the maintainers address this.

Copy the JS file from build/static/js, then place it in a script in the public folder on index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <meta name="theme-color" content="#000000" />
  <meta name="description" content="Web site created using create-react-app" />
  <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  <title>React App</title>
</head>

<body>
  <noscript>You need to enable JavaScript to run this app.</noscript>
  <div id="root"></div>
  <script src="%Your_Path_To_Label-Studio%/main.js"></script>
</body>

</html>

The script file defines a global function variable, so you can access it in React by using the window object. The useEffect hook is to make sure the initialization is only run once.

import React, { useEffect, useRef } from "react";

function App() {
  const LabelStudio = window.LabelStudio; // label-studio script stores the api globally, similar to how jQuery does
  const myLabelStudioRef = useRef(null); // store it and then pass it other components

  useEffect(() => {
    myLabelStudioRef.current = new LabelStudio("label-studio", {
      config: `
      <View>
        <Image name="img" value="$image"></Image>
        <RectangleLabels name="tag" toName="img">
          <Label value="Hello"></Label>
          <Label value="World"></Label>  
        </RectangleLabels>
      </View>
    `,

      interfaces: [
        "panel",
        "update",
        "controls",
        "side-column",
        "completions:menu",
        "completions:add-new",
        "completions:delete",
        "predictions:menu",
      ],

      user: {
        pk: 1,
        firstName: "James",
        lastName: "Dean",
      },

      task: {
        completions: [],
        predictions: [],
        id: 1,
        data: {
          image:
            "https://htx-misc.s3.amazonaws.com/opensource/label-studio/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg",
        },
      },

      onLabelStudioLoad: function (LS) {
        var c = LS.completionStore.addCompletion({
          userGenerate: true,
        });
        LS.completionStore.selectCompletion(c.id);
      },
    });
  }, []);

  return (
    <div className="App">
      {/* Use Label Studio container */}
      <div id="label-studio"></div>
    </div>
  );
}

export default App;

As far as storing the new instance of LabelStudio, there's many ways to go about it. You can store it as variable on the root component using either useState or useRef hooks and then pass it to child components. If you want to avoid manually passing variable down the component tree, then you need a state manager such as React Context or Redux.

like image 194
Caleb Taylor Avatar answered Sep 04 '25 22:09

Caleb Taylor