A new option for building interactive web interfaces has arrived in the Rust ecosystem.
WebForms Core is a technology owned and developed by Elanat. It now has a Rust implementation available as the webformscore crate, allowing Rust developers to use the WebForms Core programming model with Rust web frameworks such as Actix Web.
What is WebForms Core?
WebForms Core is a server-orchestrated UI technology in which the server generates commands that describe changes and actions to be performed on the HTML document.
At the center of the server side is the WebForms class. In Rust, the WebForms class generates WebForms Core commands. On the browser side, WebFormsJS receives those commands and executes them against the HTML DOM.
The basic architecture is:
Rust Server
↓
WebForms Class
↓
WebForms Core Commands
↓
WebFormsJS
↓
HTML DOM
This creates a clear separation between the server-side Commander and the browser-side Executor. The Rust application does not directly manipulate the browser DOM. Instead, it generates commands, while WebFormsJS performs the corresponding DOM operations in the browser.
WebFormsJS is independent of the Rust backend and can work with different server-side implementations of WebForms Core. The WebFormsJS project is available in the official WebForms Core repository.
Installing WebForms Core for Rust
The Rust implementation is available as the webformscore crate.
Install it using Cargo CLI:
cargo add webformscore
Alternatively, add it manually to your Rust project's Cargo.toml:
[dependencies]
webformscore = "2.1.0"
Then import the WebForms class and the HTML event definitions:
use webformscore::{WebForms, html_event};
The webformscore 2.1.0 crate provides the Rust implementation of the WebForms class for WebForms Core.
Installing WebFormsJS
WebFormsJS is the browser-side runtime of WebForms Core. It is installed separately from the Rust crate.
Option 1: npm
If you use npm, install WebFormsJS with:
npm install webformsjs
The official WebFormsJS repository also documents npm installation.
After installation, make the web-forms.js file available through your web server and include it in the HTML:
<script type="module" src="/static/script/web-forms.js"></script>
Option 2: GitHub repository
If you do not want to install WebFormsJS through npm, the source is available from the official repository:
The repository contains the WebFormsJS source and the web-forms.js file.
For a Rust web application, you can place the required JavaScript file under your static directory, for example:
static/
└── script/
└── web-forms.js
and reference it from the HTML page:
<script type="module" src="/static/script/web-forms.js"></script>
Option 3: Elanat Website
Go to the link below and download the corresponding version of WebFormsJS.
https://elanat.net/page_content/web_forms_js
A Rust + Actix Web Example
Let's build a simple example using Actix Web, Tera, webformscore, and WebFormsJS.
The application displays a table of student grades. When the user clicks Highlighting student grades, the browser sends a request to the Rust server. The server then generates WebForms Core commands that color the table cells according to their grades.
Here is the Rust server:
use actix_files as fs;
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
use std::sync::Arc;
use tera::Tera;
use webformscore::{WebForms, html_event};
#[derive(Clone)]
struct AppState {
tera: Arc<Tera>,
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let tera = Tera::new("templates/**/*").unwrap();
let state = AppState {
tera: Arc::new(tera),
};
HttpServer::new(move || {
let state = state.clone();
App::new()
.app_data(web::Data::new(state))
.route("/", web::get().to(index))
.route("/set-background-color", web::get().to(handle_event))
.service(fs::Files::new("/static", "./static").show_files_listing())
})
.bind("127.0.0.1:8080")?
.run()
.await
}
async fn index(state: web::Data<AppState>) -> impl Responder {
let rendered = match state.tera.render("index.html", &tera::Context::new()) {
Ok(html) => html,
Err(e) => {
eprintln!("Template error: {}", e);
return HttpResponse::InternalServerError()
.content_type("text/plain")
.body(format!("Template error: {}", e));
}
};
let mut form = WebForms::new();
form.set_get_event(
"HighlightingGrades",
html_event::ON_CLICK,
Some("/set-background-color"),
);
let body = format!(
"{}{}",
rendered,
form.export_to_html_comment(Some(true))
);
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(body)
}
async fn handle_event(_state: web::Data<AppState>) -> impl Responder {
let mut form = WebForms::new();
form.set_background_color(
"<td>*?t>:19\\<td>*?t<:20",
"darkgreen",
);
form.add_style_with_name_value(
"-",
"font-weight",
"bold",
);
form.set_text_color("-", "white");
form.set_background_color(
"<td>*?t>:17\\<td>*?t<19",
"green",
);
form.set_background_color(
"<td>*?t>:14\\<td>*?t<17",
"lightgreen",
);
form.set_background_color(
"<td>*?t>:10\\<td>*?t<14",
"khaki",
);
form.set_background_color(
"<td>*?t>:6\\<td>*?t<10",
"orange",
);
form.set_background_color(
"<td>*?t>:3\\<td>*?t<6",
"red",
);
form.set_background_color(
"<td>*?t>:0\\<td>*?t<3",
"darkred",
);
form.message_i32(
"The student's grades were successfully highlighted!",
"success",
5000,
);
HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(form.response())
}
The HTML View
The Tera template is a normal HTML document. No React component, JSX, or special WebForms Core markup is required.
<!DOCTYPE html>
<html>
<head>
<title>Using WebForms Core</title>
<script type="module" src="/static/script/web-forms.js"></script>
<style>
td {
border: 2px solid #aaa;
padding: 5px;
text-align: center;
}
</style>
</head>
<body>
<button id="HighlightingGrades">
Highlighting student grades
</button>
<table>
<thead>
<tr>
<th>Student Name</th>
<th>Math</th>
<th>Science</th>
<th>English</th>
<th>History</th>
<th>Geography</th>
<th>Average</th>
</tr>
</thead>
<tbody>
<tr>
<td>Emily Johnson</td>
<td>18</td>
<td>17</td>
<td>19</td>
<td>16</td>
<td>12</td>
<td>16.4</td>
</tr>
<tr>
<td>Michael Smith</td>
<td>15</td>
<td>16</td>
<td>14</td>
<td>17</td>
<td>15</td>
<td>15.4</td>
</tr>
<tr>
<td>Sarah Davis</td>
<td>20</td>
<td>19</td>
<td>18</td>
<td>19</td>
<td>20</td>
<td>19.2</td>
</tr>
<tr>
<td>David Brown</td>
<td>8</td>
<td>9</td>
<td>7</td>
<td>6</td>
<td>5</td>
<td>7.0</td>
</tr>
<tr>
<td>Jessica Wilson</td>
<td>17</td>
<td>18</td>
<td>16</td>
<td>18</td>
<td>17</td>
<td>17.2</td>
</tr>
<tr>
<td>Daniel Martinez</td>
<td>4</td>
<td>3</td>
<td>5</td>
<td>7</td>
<td>6</td>
<td>5.0</td>
</tr>
<tr>
<td>Ashley Taylor</td>
<td>19</td>
<td>20</td>
<td>17</td>
<td>18</td>
<td>19</td>
<td>18.6</td>
</tr>
<tr>
<td>Christopher Lee</td>
<td>9</td>
<td>8</td>
<td>7</td>
<td>13</td>
<td>4</td>
<td>8.2</td>
</tr>
<tr>
<td>Kevin Anderson</td>
<td>7</td>
<td>10</td>
<td>12</td>
<td>2</td>
<td>8</td>
<td>7.8</td>
</tr>
<tr>
<td>Laura Thomas</td>
<td>3</td>
<td>7</td>
<td>6</td>
<td>4</td>
<td>9</td>
<td>5.8</td>
</tr>
<tr>
<td>Brian White</td>
<td>10</td>
<td>8</td>
<td>7</td>
<td>9</td>
<td>6</td>
<td>8.0</td>
</tr>
<tr>
<td>Megan Harris</td>
<td>7</td>
<td>5</td>
<td>3</td>
<td>8</td>
<td>4</td>
<td>5.4</td>
</tr>
</tbody>
</table>
</body>
</html>
What Happens When the Page Loads?
When the page is first requested, the browser sends:
GET /
The index() function renders the Tera template and creates a WebForms instance:
let mut form = WebForms::new();
The following line:
form.set_get_event(
"HighlightingGrades",
html_event::ON_CLICK,
Some("/set-background-color"),
);
associates the click event of the HTML element whose ID is HighlightingGrades with the server endpoint:
/set-background-color
The generated WebForms Core commands are then appended to the initial HTML response:
let body = format!(
"{}{}",
rendered,
form.export_to_html_comment(Some(true))
);
WebFormsJS detects these commands in the HTML response and prepares the client-side behavior.
No custom JavaScript event handler is required.
The page after the initial load
What Happens When the Button Is Clicked?
When the user clicks:
<button id="HighlightingGrades">
Highlighting student grades
</button>
WebFormsJS sends a request to:
/set-background-color
Actix Web routes that request to:
async fn handle_event(...)
The Rust server now creates a new WebForms instance and generates a sequence of commands.
For example:
form.set_background_color(
"<td>*?t>:19\\<td>*?t<:20",
"darkgreen",
);
This uses a WebForms Core WebForms Place Criteria expression to target table cells according to their text value.
The other commands progressively assign colors to different grade ranges:
19–20 → dark green
17–19 → green
14–17 → light green
10–14 → khaki
6–10 → orange
3–6 → red
0–3 → dark red
The following commands:
form.add_style_with_name_value("-", "font-weight", "bold");
form.set_text_color("-", "white");
use the previous target and make the selected cells bold with white text.
Finally:
form.message_i32(
"The student's grades were successfully highlighted!",
"success",
5000,
);
generates a WebForms Core message that is displayed for 5000 milliseconds.
The server then returns:
form.response()
The important point is that the server does not need to return the complete HTML table again.
It returns WebForms Core commands.
WebFormsJS receives those commands and executes them against the existing DOM.
The result after clicking the button
Rust Controls the Interaction, WebFormsJS Executes It
This example demonstrates the central idea behind WebForms Core:
Rust / Actix Web
│
▼
WebForms Class
│
▼
WebForms Core Commands
│
▼
WebFormsJS
│
▼
HTML DOM
The Rust backend remains responsible for the interaction logic, while the browser runtime handles DOM execution.
Instead of requiring a separate frontend application to orchestrate these interactions, WebForms Core allows the server to generate executable UI commands while keeping the HTML as the actual document.
With WebForms Core, the HTML can remain ordinary HTML, while the server can generate precise commands to manipulate that HTML.
A New Option for Rust Web Development
The arrival of webformscore 2.1.0 gives Rust developers another way to build interactive web applications.
You can use:
- Rust for server-side application logic
- Actix Web or another Rust web framework for HTTP handling
- Tera or another template engine for HTML generation
- WebForms Core for server-generated UI commands
- WebFormsJS for browser-side execution
- HTML as the actual UI document
The result is a server-driven UI architecture without requiring a separate frontend project.
For Rust developers interested in exploring this approach, the webformscore crate is now available, while WebFormsJS provides the browser-side runtime required to execute the generated commands.
WebForms Core is bringing a different approach to interactive web development in Rust: let Rust decide what the interface should do, and let WebFormsJS execute those instructions in the browser.
Related links
In Elanat:
in GitHub:
in Crates:



Top comments (0)