Creating shareable React widgets
Tags: react, tutorial
Published: 2022-02-02 15:00:45

**Written by [Vijit Ail](https://blog.logrocket.com/author/vijitail/)✏️**

React is a component-based JavaScript library that continues to grow in popularity. In a 2021 worldwide study, [over 40 percent of surveyed developers reported using React](https://www.statista.com/statistics/1124699/worldwide-developer-survey-most-used-frameworks-web/). The widespread usage of React shouldn’t come as a surprise. React is flexible, easy to learn, and offers the ability to write custom components. These custom components, or [individual UI blocks](https://blog.logrocket.com/building-reusable-ui-components-with-react-hooks/), are reusable and may be easily shared among other React apps.

One aspect of React that is less straightforward, however, is the integration of custom components into a static website. In this tutorial, we’ll demonstrate how to use widgets to integrate a React component with static websites built entirely from HTML.

Open up your favorite text editor, and let's get started!

## What are widgets?

A widget is a piece of UI that handles data logic and data presentation internally and independently of other elements on the webpage. Widgets are used to add dynamic content (such as a popup, image carousel, or dynamic list) to a standalone, static application.

Widgets are framework agnostic. This tutorial focuses on the integration of a React component, but the same method may be used with a component created in a framework of your choosing.

A weather widget is an example of a common widget that many people interact with daily. It displays current weather conditions in nearby locations using the user’s geolocation. This widget handles several tasks, such as obtaining permission to access user location and fetching weather data.

An application or website that embeds a weather widget does not need to be concerned with how the data is fetched or how it is displayed to the user. These tasks are handled by the widget.

## Creating a widget

Let’s create a widget that will read data from the [Crypto Compare API](https://min-api.cryptocompare.com) and display the list of top cryptocurrencies by market cap.

First, we’ll need to set up a React project.

To create and run a React application, Node.js and npm must both be installed in the system.

Open the terminal, and run the following command:

```BASH
npx create-react-app ReactWidgetDemo
cd ReactWidgetDemo 
```

Inside the `src` folder, create two new folders: `components` and `hooks`. We’ll create a custom hook to fetch data from the Crypto Compare API.

Inside the `hooks` folder, create a new file: `useCryptoData.js`.

```js
import { useEffect, useState } from "react";

const useCryptoData = () => {
  const [cryptoData, setCryptoData] = useState([]);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    setLoading(true);
    // fetch the data and set cryptData
    setLoading(false);
  }, [])

  return { cryptoData, isLoading }
};
```

In the `useCryptoData` custom hook, we employ the `useState` React hook to create two state variables: `cryptoData` and `isLoading`.

The `cryptoData` state variable will store the data from the API. The `isLoading` state will indicate if the data fetching is in progress.

Now, we’ll use the `fetch()` method to retrieve data from the Crypto Compare API and then set the `cryptoData` state:

```js
import { useEffect, useState } from "react";

const useCryptoData = () => {
  const [cryptoData, setCryptoData] = useState([]);
  const [isLoading, setLoading] = useState(true);
  useEffect(() => {
    setLoading(true);
    fetch(
      "https://min-api.cryptocompare.com/data/top/mktcapfull?limit=10&tsym=USD"
    )
      .then((res) => res.json())
      .then((data) => {
        console.log(data);
        const preparedData = [];
        data.Data.forEach((d) => {
          const { Id, Name, FullName, ImageUrl, Url } = d.CoinInfo;
          let Price, Change24hr;
          if (d.DISPLAY?.USD) {
            const { PRICE, CHANGEPCT24HOUR } = d.DISPLAY.USD;
            Price = PRICE;
            Change24hr = CHANGEPCT24HOUR;
          }
          preparedData.push({
            Id,
            Name,
            FullName,
            ImageUrl: `https://www.cryptocompare.com${ImageUrl}`,
            Url: `https://www.cryptocompare.com${Url}`,
            Price,
            Change24hr
          });
        });
        setCryptoData(preparedData);
      })
      .finally(() => setLoading(false));
  }, []);
  return { cryptoData, isLoading };
};

export default useCryptoData;
```

The `components` folder will house the main widget component file. Import the `useCryptoData` hook in the `CryptoList` component file:

```jsx
import useCryptoData from "./useCryptoData";

const CryptoItem = (props) => (
  <div className="item">
    <img src={props.ImageUrl} className="icon" alt={props.Name} />
    <div className="display-container">
      <div className="name">{props.Name}</div>
      <div className="fullname">{props.FullName}</div>
    </div>
    <div className="price-container">
      <div className="price">{props.Price}</div>
      <div
        className={`price-change ${
          parseInt(props.Change24hr) < 0 ? "danger" : "success"
        }`}
      >
        {props.Change24hr}
      </div>
    </div>
  </div>
);

const CryptoList = () => {
  const { cryptoData, isLoading } = useCryptoData();
  return (
    <div>
      <div className="container">
        {!isLoading ? (
          cryptoData.map((itemData) => (
            <CryptoItem key={itemData.Id} {...itemData} />
          ))
        ) : (
          <p className="loading-text">Loading Data...</p>
        )}
      </div>
    </div>
  );
};

export default CryptoList;
```

Next, use the `CryptoList` component inside the main `App()` component:

```jsx
import CryptoList from "./components/CryptoList";
import "./styles.css";

export default function App() {
  return (
    <div>
      <CryptoList />
    </div>
  );
}
```

Now, let’s add styling to the component to improve its appearance:

```css
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@500;600&display=swap");
:root {
  --dark: #1e2329;
  --light: #fafafa;
  --success: #03a66d;
  --danger: #cf304a;
}
* {
  font-family: "Open Sans", sans-serif;
}
.name,
.loading-text {
  color: var(--light);
  font-size: 16px;
  font-weight: 600;
}
.fullname {
  color: #b6b6b6;
  font-size: 14px;
  margin-top: 3px;
  font-weight: 500;
}
.item {
  display: flex;
  align-items: center;
  padding: 12px 0px;
  border-bottom: 1px solid #949191;
}
.item:first-child {
  padding-top: 0px;
}
.item:last-child {
  padding-bottom: 0px;
  border-bottom: 0px;
}
.container {
  background-color: var(--dark);
  padding: 20px;
  border-radius: 12px;
  box-shadow: rgba(0, 0, 0, 0.1) 0px 10px 30px;
}
.icon {
  height: 24px;
  width: 24px;
  margin-right: 14px;
}
.price-container {
  margin-left: auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.price {
  font-weight: 500;
  color: var(--light);
  font-size: 16px;
}
.price-change {
  margin-top: 3px;
}
.price-change.danger {
  color: var(--danger);
}
.price-change.success {
  color: var(--success);
}
```

To get the React application up and running, use the following command from the project root:

```BASH
npm run start
```

This will set up a local dev server and run the application on port 3000.

Open the browser and go to http://localhost:3000.

Go to CodeSandbox to [see the demo `CryptoList`widget in action](https://codesandbox.io/s/intelligent-jones-zwd0h).

## Using a widget

Now it’s time to use the demo `CryptoList` widget in a standalone static HTML webpage. We’ll use an iframe to embed the widget.

We’ll pass the React application URL to the `src` attribute of the `<iframe />`. In this example, the URL is http://localhost:3000.

```html
<iframe
  src="http://localhost:3000"
  style="border: none;"
  width="100%"
></iframe>
```

Here’s the iframe and widget code included with other elements in the static webpage:

```html
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .row {
        display: flex;
        flex-direction: row;
      }
      .col {
        flex: 1;
      }
    </style>
    <title>Static website</title>
  </head>
  <body style="min-height: 100vh">
    <div class="row">
      <div class="col">
        <div>Excepteur sint occaecat cupidatat non proident.</div>
        <iframe
          src="http://localhost:3000"
          style="border: none; min-height: 98vh"
          width="100%"
        ></iframe>
      </div>
      <div class="col">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.
      </div>
    </div>
  </body>
</html>
```

Here’s the demo `CryptoList` widget shown embedded in the HTML webpage:

![React Demo CryptoList Widget](https://blog.logrocket.com/wp-content/uploads/2022/01/react-demo-widget.png)

## Sharing components between React apps

Depending on the project it may be necessary to share widgets between React apps, rather than integrating them into a static website. To create shareable widgets for React Apps, we can [create a common component library](https://blog.logrocket.com/build-component-library-react-typescript/) and publish it on npm. This guide contains detailed instructions on [how to create an npm package for React](https://blog.logrocket.com/the-complete-guide-to-publishing-a-react-package-to-npm/).

Alternatively, we can import the package from a repository:

```BASH
npm i https://github.com/{username}/{repo}.git
```

We can install the package in different React projects and import the component in our JSX files. With this approach, the widget can be maintained separately. Whenever the widget’s functionality or styling is updated, this will be reflected in the React project by simply updating the npm package.

## Conclusion

It’s very easy to make shareable widgets in React. In this tutorial, we demonstrated how to use React to build a simple widget and then integrate it with a static HTML webpage.

As a developer, it’s important to understand the pros and cons of embedding widgets using an iframe. This may not be the preferred option for every use case. For projects that involve a large number of embedded widgets, consider [migrating to Gatsby](https://blog.logrocket.com/is-gatsby-really-that-great-e7b19c4c1c05/) or another static-site generator for React.

To learn more about React, see its [website](https://reactjs.org/docs/getting-started.html) or the React resources on [MDN](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started).

---

## Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket](https://www2.logrocket.com/react-performance-monitoring). 

![LogRocket signup](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free](https://www2.logrocket.com/react-performance-monitoring).