With the development of server-oriented development, when it is necessary to take templates from the server, there is a need to send HTML code to the client. In this article, we will consider the main ways to do this. All of these ways will use javascript, but their essence can be reflected in other programming languages.
Let's move directly to the code and see how this can be implemented in projects.
Backend
Taking into account the backend, it is worth noting that on the client we must receive templates from somewhere. To do this, we can create an API route, through which we will take HTML from the server. First, let's create the HTML code that needs to be sent to the client:
form.html
<h1>Contact Us</h1>
<form action="/submit" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br><br>
<label for="message">Message:</label><br>
<textarea id="message" name="message" rows="4" cols="50" required></textarea><br><br>
<button type="submit">Submit</button>
</form>
This is a simple contact form. It would be cool if it could be edited on the server, and then on the client, and not only on one site, but also, let's say, on several related sites.
To do this, let's create an API route, through which we will receive HTML from the server. For the backend part, we will use Node.js. The framework on which we will do this is Express.js. It is one of the most popular today and it is perfect for the task we are solving. First, we specify the controller that will process the HTML:
const express = require("express");
const expressRouter = express.Router();
const path = require("path");
const formController = (req, res) => {
res.sendFile(path.join(__dirname, "../form.html"));
};
expressRouter.use("/getForm", formController);
Then, you need to connect the controller to /api
, so as not to mix the regular site routes with the backend ones. In express.js there is an application entry point where everything is initialized. This is where we need to import our controller:
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/formController");
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 that, we should have a route /api/getForm
on the site, where we can send GET
requests. In response, we will get HTML.
Client side
Here we can highlight several ways of getting HTML from the server. All of them will be based on sending a request to the server. In javascript there is a fetch
construct that allows you to do something similar (XMLHTTPRequest
is not considered, since it is already old. If you do not need to support old browsers, then I strongly recommend not to use it).
1. Using a third-party library
In order to load HTML from the server we can use the HMPL, which is a template language for displaying UI from server to client. It is based on customizable requests sent to the server and processed into ready-made HTML.
To use it, let's install hmpl-js
as a dependency and create a script:
npm i hmpl-js
After that, we import the compile
function:
import { compile } from "hmpl-js";
const templateFn = compile(
`{{ src: "/api/getForm" }}`
);
const form = templateFn();
Result:
form = {
response: <template><h1>Contact Us</h1><form action="/submit" method="...</template>,
status: 200
}
Here, we get a template
, where our form is stored. You can also add a div
to the string, then you will get without a template
, but directly in the div
.
import { compile } from "hmpl-js";
const templateFn = compile(
`<div>{{ src: "/api/getForm" }}</div>`
);
const form = templateFn();
/*
form = {
response: <div><h1>Contact Us</h1><form...</div>,
status: 200
}
*/
In this case, we immediately get an element that we can add to the DOM.
Features (Advantages):
Reusability:
You will be able to reuse a template created once as many times as you want, just like, for example, making instances of aclass
.Simple and clear syntax:
The HTML introduces a request object, which is completely similar in syntax to vanilla, so when working in js you can easily copy the code and not worry, since the module works on JSON5.Customizable:
The template language offers a wide range of request customization. Unlike similar projects like HTMX, you can almost completely control the flow.Lightweight:
It weighs a little, about 15+ kilobytes, which will have almost no impact on the project.Browser Compatibility:
Modern JavaScript APIs likefetch()
are widely supported in browsers, ensuring compatibility without extra polyfills for most use cases.
Drawbacks:
Dependencies:
By connecting a module, you connect additional code to the project, which weighs a certain number of bytes.Modern API Limitation in Older Browsers:
Although widely supported, features likefetch()
may require polyfills for older browsers, unlike some libraries that handle backward compatibility.
2. Using default js
First, you need to create a script file and make a request to the server there. The code in the file will be approximately as follows:
main.js
fetch('/api/getForm') // URL to the HTML snippet
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.text();
})
.then(html => {
console.log(html)
})
.catch(error => {
console.error('Error fetching HTML:', error);
});
Here we get a response from the server and if it is successful, we call the text
function to receive it. It is worth considering that there are HTTP codes 100, where there is mainly a redirection, so when processing them you also need to be more careful and add error handling.
You can find out more about HTTP codes here.
Features (Advantages):
Lightweight and No Dependencies:
Using plain JavaScript eliminates the need for additional libraries or frameworks, reducing the project's overall size and dependencies.Full Control:
Plain JavaScript gives you complete control over the implementation details, allowing you to handle network requests, responses, and DOM manipulations exactly as needed.Browser Compatibility:
Modern JavaScript APIs likefetch()
are widely supported in browsers, ensuring compatibility without extra polyfills for most use cases.Learning Opportunity:
Working with raw JavaScript helps developers understand fundamental concepts, such as HTTP requests, responses, and the DOM, improving their programming skills.
Drawbacks:
Verbose Code:
Plain JavaScript often requires more boilerplate code compared to libraries like Axios or frameworks like React, which can handle requests and updates more succinctly.Error Handling:
Managing errors (e.g., network issues, invalid responses) can become complex and repetitive without the abstractions provided by higher-level libraries.Lack of Abstraction:
Tasks like handling timeouts, retries, or concurrent requests must be implemented manually, which can be tedious and error-prone.Maintainability:
Code written purely in JavaScript may be harder to maintain in larger projects due to its verbosity and lack of standardized patterns.Modern API Limitation in Older Browsers:
Although widely supported, features likefetch()
may require polyfills for older browsers, unlike some libraries that handle backward compatibility.
Conclusion
Depending on the situation, you can use either the first approach or the second. In the second one, you have full control over the process, but it is still not suitable when you need to work with several components, because you will have to create your own logic, and this takes up time.
Thank you all for reading! I hope you found this article helpful!
Also, I will be glad if you support the project with your star. Thank you!
Top comments (3)
Great Share
Thanks you!
It is worth noting that instead of pure fetch, you can also use other libraries for sending requests that specialize in this.