Reusable Components Across Multiple Sites: From Design System to Deployment 🔥
Anthony Max

Anthony Max @anthonymax

About: 🐜 Fetch API enjoyer

Joined:
Sep 20, 2024

Reusable Components Across Multiple Sites: From Design System to Deployment 🔥

Publish Date: Aug 10
156 8

TL;DR

When a developer makes one site for a long time, it is clear that few people think about such functionality, but imagine that your service has become popular and you want to expand.

It would seem that one repository would be enough for work, but if there are 10 sites from one service (company), then the same components will have to be used in all of them, since creating a new design is simply unprofitable. Users of services such as GitHub and YouTube cannot even imagine how many subsites there may be.

Web development in 2010 and in 2025 are almost diametrically different today. Therefore, it is important to understand what modern practices exist today. And one of them is component sharing. In this article, we will talk about it!


🔩 Component

For example, we can take the button component, which we will transfer between the components. It will look like this:

<button class="button">Click Me</button>
<style>
  .button {
    background-color: #4caf50;
    color: white;
    border: none;
    padding: 12px 24px;
    text-align: center;
    text-decoration: none;
    font-size: 16px;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s, transform 0.2s;
  }

  .button:hover {
    background-color: #45a049;
  }

  .button:active {
    transform: scale(0.95);
  }
</style>
Enter fullscreen mode Exit fullscreen mode

The result on the site will be as follows:

button

Now, let's take two sites for which you need to make the component common. Let it be example1 example2. They can all be hosted on different hosting sites. Let one be deployed from GithHub, and the other from some local hosting.

Now, the main question arises - how to share?


🛤️ Several ways of sharing

I will describe several methods that are usually used for this. From the most banal to the most practical.

Sharing components

Both of these approaches will look something like this.


🔗 1. Output to a file and connection by script

This method assumes that there will be a function that returns HTML markup. And, this function can be connected via a file remotely. It doesn't matter where this file will be located. You just need to connect it from there.

createButton.js

// buttonModule.js
(function (global) {
  // Define the createButton function
  function createButton() {
    // Create a <style> element and add styles
    const style = document.createElement('style');
    style.textContent = `
      .button {
        background-color: #4caf50;
        color: white;
        border: none;
        padding: 12px 24px;
        text-align: center;
        text-decoration: none;
        font-size: 16px;
        border-radius: 5px;
        cursor: pointer;
        transition: background-color 0.3s, transform 0.2s;
      }
      .button:hover {
        background-color: #45a049;
      }
      .button:active {
        transform: scale(0.95);
      }
    `;

    // Create the button element
    const button = document.createElement('button');
    button.className = 'button';
    button.textContent = 'Click Me';

    // Return the elements (style and button)
    return { style, button };
  }

  // Expose the function to the global scope
  global.buttonModule = {
    createButton,
  };
})(window);
Enter fullscreen mode Exit fullscreen mode

example1/root/index.html,
example2/root/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Button Module</title>
  <script src="https://.../buttonModule.js"></script>
</head>
<body>
  <div id="wrapper"></div>
  <script>
    // Use the buttonModule
    const { style, button } = buttonModule.createButton();
    const wrapper = document.getElementById("wrapper");
    wrapper.append(style); // Attach styles to the document
    wrapper.append(button); // Add the button to the page
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Here we connect the module via a site that is not connected to our two sites. It could be the same GitHub.

Features (Advantages):

  • Easy to implement with standard <script> tags in HTML without additional setup.
  • Requires no modern tools or configurations like Webpack or Vite.
  • Suitable for small, single-page applications or quick experiments.
  • Minimal setup, enabling faster development.
  • Can be seamlessly integrated into existing projects that already rely on global variables.

Drawbacks:

  • If there are many components, there will be a thousand scripts, which makes this method suitable only for a single use.
  • Adds variables or objects to the global scope, increasing the risk of naming conflicts.
  • Difficult to avoid clashes when multiple scripts are used.
  • Makes the project harder to scale or refactor.
  • Scripts depend on the correct order of loading, which must be manually managed.
  • Less maintainable and not aligned with current best practices.

🌐 2. Using a third-party library and moving the component to the API

For this method, we will use a module such as HMPL. It will allow you to connect components from the server using simple templates based on objects. To begin with, let's take the component to the server. Create a separate HTML and give it via API request. What will the .html file look like:

button.html

<button class="button">Click Me</button>
<style>
  .button {
    background-color: #4caf50;
    color: white;
    border: none;
    padding: 12px 24px;
    text-align: center;
    text-decoration: none;
    font-size: 16px;
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s, transform 0.2s;
  }

  .button:hover {
    background-color: #45a049;
  }

  .button:active {
    transform: scale(0.95);
  }
</style>
Enter fullscreen mode Exit fullscreen mode

After that, we will need to somehow transfer this file to the server. Let the backend be on Node.js. We will use express.js as one of the most popular frameworks for creating API. First, we will set the route by which we will receive our component:

buttonController.js

const express = require("express");
const expressRouter = express.Router();
const path = require("path");

const buttonController = (req, res) => {
  res.sendFile(path.join(__dirname, "../button.html"));
};

expressRouter.use("/getButton", buttonController);
Enter fullscreen mode Exit fullscreen mode

app.js

const express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const cors = require("cors");
const PORT = 8000;
const app = express();
const routes = require("./routes/buttonController");

app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors({ origin: true, credentials: true }));

app.set(express.static(path.join(__dirname, "src")));

app.use("/api", routes);

app.listen(PORT);
Enter fullscreen mode Exit fullscreen mode

After this, we will have a route by which we can now easily take the component. On the site we connect HMPL. It can be connected in several ways, let's consider the main ones:

Via script

<script src="https://unpkg.com/json5/dist/index.js"></script>
<script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js"></script>
<script src="https://unpkg.com/dompurify/dist/purify.min.js"></script>
Enter fullscreen mode Exit fullscreen mode

Via import

import hmpl from "hmpl-js";
Enter fullscreen mode Exit fullscreen mode

We use method 1, since index.html is the default on our sites.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Button Module</title>
  </head>
  <body>
    <script src="https://unpkg.com/json5/dist/index.js"></script>
    <script src="https://unpkg.com/hmpl-js/dist/hmpl.min.js">
    <script src="https://unpkg.com/dompurify/dist/purify.min.js"> 
    </script>
</script>
    <script>
      const templateFn = hmpl.compile(
        `<div id="wrapper">
          {{#request src="https://.../api/getButton"}}
          {{/request}}
        </div>`
      );
      const btnWrapper = templateFn().response;
      document.body.append(btnWrapper);
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Here we do almost the same as in the first method, but the trick is that now you can safely reuse the component. Let's say you can do it like this:

const btnWrapper1 = templateFn().response;
const btnWrapper2 = templateFn().response;
Enter fullscreen mode Exit fullscreen mode

Also, there is a lot of additional functionality that comes from the module - indicators, request error handling, etc. Since the module is based on fetch, you can effectively customize requests and do much more.

Features (Advantages):

  • Reusing components
  • Suitable for both small and large applications with thousands of components
  • A huge amount of functionality aimed specifically at this server-oriented approach to components displayed on the client
  • Flexibility in use

Drawbacks:

  • Connecting two script files
  • Creating an additional API

This approach kind of implements the SSR approach, but on the client without the key element for it - visibility for robots, but otherwise - it's a cool method that can make the solution of the problem easier.


✅ Conclusion

Depending on the situation, you can use either the first approach or the second. In the first one, you have full control over the process, but it is still not suitable when you need to work with several components, since you will have to constantly import files, which is not good.


Thank you all for reading! I hope you found this article helpful ❤️!

P.S. Also, don't forget to help me and star HMPL!

🌱 Star HMPL

Comments 8 total

  • Anthony Max
    Anthony MaxAug 10, 2025

    Reusing components is a very important part of building a service ecosystem.

    • senol kalyoncu
      senol kalyoncuAug 11, 2025

      Web Partner İstanbul > Web Tasarım ve E-Ticaret sistemleri çözüm merkezi olarak Seo ve Google reklam alanında size destek olabiliriz. WebPartner iletişim bilgilerine web sitemizden ulaşabilirsiniz.

  • Butterfly
    ButterflyAug 10, 2025

    Interesting article!

  • Fraz Khan
    Fraz KhanAug 10, 2025

    Sharing UI components across multiple sites scales best when you start simple and graduate to packaged, versioned components (npm/CDN + Storybook), then adopt a monorepo or runtime composition (Module Federation) only as team size and deployment independence demand it.

    • Start small: remote script or snippet for fast experiments.
    • Mid-scale: publish versioned packages (npm/CDN) and use Storybook + design tokens for discoverability and theming.
    • Large-scale: use workspaces/monorepo or Module Federation for independent teams and runtime composition.
  • 𒎏Wii 🏳️‍⚧️
    𒎏Wii 🏳️‍⚧️Aug 11, 2025

    Interesting article, but I find the example a bit unfortunate.

    There is already a button component in HTML: <button>; you style it like this: button {}

    There's good reasons why many big frameworks extract styles into classes that have to be explicitly set by users rather than styling elements without being asked to, but in this case that's just a whole lot of unnecessary complexity. The way you style a button is with one block of code in your CSS file.

    The easiest example I can think of would be a toggle-button: It isn't already a thing in HTML and it needs a bit of logic rather than just styling; but it's still simple enough for an example.

  • Anik Sikder
    Anik SikderAug 11, 2025

    This is a great breakdown of the challenges and practical solutions for sharing UI components across multiple sites! I especially appreciate how you started from simple script-based sharing and moved towards more scalable, API-driven approaches.

    The global script method is super handy for quick demos or small projects, but as you pointed out, it quickly becomes a maintenance headache with multiple components and potential naming collisions. Moving components to an API or using a dedicated design system sounds like the way to go for bigger apps or multiple teams.

    I’d love to see a follow-up diving deeper into how to version and manage component updates across sites, maybe with tools like Storybook or Bit.dev in the mix.

    Thanks for sharing these insights reusable components are truly the backbone of efficient modern web development!

Add comment