DEV Community

Cover image for Creating shareable React widgets
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

Creating shareable React widgets

Written by Vijit Ail✏️

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. 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, 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 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:

npx create-react-app ReactWidgetDemo
cd ReactWidgetDemo 
Enter fullscreen mode Exit fullscreen mode

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.

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 }
};
Enter fullscreen mode Exit fullscreen mode

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:

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;
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

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

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

export default function App() {
  return (
    <div>
      <CryptoList />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

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

@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);
}
Enter fullscreen mode Exit fullscreen mode

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

npm run start
Enter fullscreen mode Exit fullscreen mode

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 CryptoListwidget in action.

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.

<iframe
  src="http://localhost:3000"
  style="border: none;"
  width="100%"
></iframe>
Enter fullscreen mode Exit fullscreen mode

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

<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>
Enter fullscreen mode Exit fullscreen mode

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

React Demo CryptoList Widget

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 and publish it on npm. This guide contains detailed instructions on how to create an npm package for React.

Alternatively, we can import the package from a repository:

npm i https://github.com/{username}/{repo}.git
Enter fullscreen mode Exit fullscreen mode

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 or another static-site generator for React.

To learn more about React, see its website or the React resources on MDN.


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.

LogRocket signup

LogRocket 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.

Latest comments (0)