It is not uncommon to encounter such a task when creating a web application, when it is necessary to transfer a component between sites. Usually, these are some kind of general buttons, blocks like footer, header, and the like.
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>
The result on the site will be as follows:
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.
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);
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>
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>
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);
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);
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>
Via import
import hmpl from "hmpl-js";
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>
<script>
const templateFn = hmpl.compile(
`<div id="wrapper">{{ src: "https://.../api/getButton" }}</div>`
);
const btnWrapper = templateFn().response;
document.body.append(btnWrapper);
</script>
</body>
</html>
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;
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!
Top comments (4)
Hi, @anthonymax ,
This is really a good content. You added 2 approaches here. There are few more approaches are there.
npm registry
and later when you need in any project then install and import it to reuse.bit.dev
and you can reuse it.Hello! Yes, you can do it via npm, but I thought that since the first method is almost the same, I thought that it was not worth adding additionally. In theory, you can do it on npm, and in any other service that provides access to the file via a link.
Yes, from a performance perspective, importing a reusable component from a package manager registry is almost similar to adding it via a script tag. The main difference is that in the former case, the initial build size may be larger, but there are no additional network calls. Additionally, managing dependencies with other external libraries is easier when using a component imported from package manager registry, compared to directly adding them via a script tag.
Thanks for this beautiful content.
Your
hmpl
library is really good for performance perspective using proxy server.Thank you very much! I just think that in npm the components will be common to everyone, and with the library you can just send credentials and take html from the server with protection.