C is one of the most influential programming languages in software development. It is widely used for operating systems, embedded systems, networking, infrastructure, and performance-critical software.
But can C also be used to build an interactive web application where the server controls the HTML interface?
Yes. And the server can control the HTML interface without requiring a separate frontend application.
WebForms Core (WFC) is a server-side UI manipulation technology developed by Elanat. It allows a server application to generate commands that manipulate the HTML DOM in the browser.
The basic architecture is:
C Server
↓
WebForms header
↓
Commands
↓
WebFormsJS
↓
HTML DOM
This article shows how to build a web application in C using Mongoose as the HTTP server and WebForms Core for server-side UI manipulation.
WebForms Core in C
The C implementation is provided as a single header file, WebForms.h.
This makes integration straightforward. The application does not need a specific C package manager for WebForms Core.
For the HTTP server, this example uses Mongoose:
#include "mongoose.h"
#include "WebForms.h"
Mongoose handles HTTP requests and responses, while WebForms Core generates commands for manipulating the browser interface.
The resulting stack is:
C Application
│
├── Mongoose
│ └── HTTP Server
│
└── WebForms Core
└── UI Commands
↓
WebFormsJS
↓
HTML DOM
Download the mongoose.h and mongoose.c files directly from the link below:
https://github.com/cesanta/mongoose
The C WebForms header is available in the WebForms Core GitHub repository:
https://github.com/webforms-core/Web_forms_classes/tree/elanat_framework/c
You can get WebForms.h directly from the c directory and place it in your C project.
This makes the WebForms Core C implementation easy to integrate into projects using different build systems or development environments.
Building the Web Page in C
The HTML page can simply be stored as a C string:
static const char *view_html =
"<!DOCTYPE html>\n"
"<html>\n"
"<head>\n"
" <title>Using WebForms Core</title>\n"
" <script type=\"module\" src=\"/script/web-forms.js\"></script>\n"
"</head>\n"
"<body>\n"
" <form method=\"post\" action=\"/\">\n"
" <label for=\"txt_Name\">Your Name</label>\n"
" <input name=\"txt_Name\" id=\"txt_Name\" type=\"text\" />\n"
" <br>\n"
" <label for=\"txt_FontSize\">Set Font Size</label>\n"
" <input name=\"txt_FontSize\" id=\"txt_FontSize\" type=\"number\" />\n"
" <br>\n"
" <label for=\"txt_BackgroundColor\">Set Background Color</label>\n"
" <input name=\"txt_BackgroundColor\" id=\"txt_BackgroundColor\" type=\"text\" />\n"
" <br>\n"
" <input name=\"btn_SetBodyValue\" type=\"submit\" value=\"Click to send data\" />\n"
" </form>\n"
"</body>\n"
"</html>";
There is no JSX, no frontend component framework, and no separate frontend application.
The page is standard HTML.
Controlling the HTML from C
When the form is submitted, Mongoose provides the POST data:
char fontSize_str[32] = {0};
char background_color[128] = {0};
char name[128] = {0};
mg_http_get_var(
&hm->body,
"txt_FontSize",
fontSize_str,
sizeof(fontSize_str)
);
mg_http_get_var(
&hm->body,
"txt_BackgroundColor",
background_color,
sizeof(background_color)
);
mg_http_get_var(
&hm->body,
"txt_Name",
name,
sizeof(name)
);
A WebForms instance can then be created:
WebForms *form = webforms_create();
The server creates InputPlace selectors:
char *ipForm = webforms_ip_tag("form");
char *ipH3 = webforms_ip_tag("h3");
char *ipBtn = webforms_ip_tag("btn_SetBodyValue");
And generates UI commands:
webforms_set_font_size(form, ipForm, fontSize);
webforms_set_background_color(
form,
ipForm,
background_color
);
webforms_set_disabled(
form,
ipBtn,
true
);
webforms_add_tag(
form,
ipForm,
"h3",
""
);
webforms_set_text(
form,
ipH3,
welcome
);
The C server is therefore not simply returning data. It is generating instructions for the browser.
Screenshot of page load
Screenshot after send data
Low-Overhead Server Commands
One of the interesting ideas behind WFC is that the server sends commands, rather than continuously sending a representation of the entire UI.
The commands are compact and can be embedded inside HTML comments:
Commands with html
...
<b> ... </b>
...
<div> ... </div>
...
<!--[web-forms]
st<h1>=WebForms Core in C!
bc<body>=lightblue-->
This has two useful properties.
First, the commands do not interfere with the visible HTML structure.
Second, the command representation has relatively low overhead because only the required UI operations need to be transmitted.
Commands without html
[web-forms]
fs<form>=26px
bc<form>=cadetblue
sd<btn_SetBodyValue>=1
nt<form>=h3
st<h3>=Welcome Adriano!
WebFormsJS detects the commands and executes them against the DOM.
Server
↓
Compact Commands
↓
WebFormsJS
↓
DOM Operations
A Different Approach to Stateless Server-Driven UI
Before WebForms Core, different systems and approaches attempted to combine server-side UI control with a stateless server and command-based communication.
This model has an obvious challenge:
How can multiple asynchronous interactions remain synchronized without requiring the server to maintain the complete client state?
WFC takes a different approach.
Several mechanisms work together to control the execution flow.
Queue Manager
Requests can be placed into a queue and processed serially.
Request 1
↓
Request 2
↓
Request 3
This helps preserve the order of UI operations.
Loader
When an operation depends on a server response, a Loader can temporarily lock the relevant interface before the result arrives.
This prevents the user from triggering conflicting interactions while the required server operation is still pending.
Serial Request Processing
Queued requests are processed in order, helping dependent UI operations execute in a predictable sequence.
DebounceDelay
For events that can fire repeatedly, DebounceDelay can prevent unnecessary requests from being generated too rapidly.
Together, these mechanisms address an important problem in interactive Server-Driven UI systems: synchronization between user actions, server responses, and client-side command execution.
Stateless by Design
WFC is, by itself, a stateless server-side UI architecture.
The server does not need to maintain a complete representation of the browser DOM.
A typical flow can therefore remain:
HTTP Request
↓
C Server
↓
WebForms Commands
↓
HTTP Response
↓
Browser
However, Stateless does not mean that server-side state is forbidden.
An application can still maintain state on the server when a particular page or scenario requires it.
The choice depends on the application's requirements.
Memory Management in C
Because this implementation is written in C, memory management is explicit.
For example:
WebForms *form = webforms_create();
creates a WebForms instance, while:
webforms_clean(form);
webforms_free(form);
cleans and releases it.
InputPlace values returned by:
webforms_ip_tag("form");
are heap-allocated strings and therefore must also be released:
free(ipForm);
free(ipH3);
free(ipBtn);
The response returned by:
char *response = webforms_response(form);
is also heap-allocated and must be released after sending it:
free(response);
This keeps the API compatible with the explicit memory-management model expected in C.
Running the C Web Server
Mongoose can listen for HTTP connections with:
mg_http_listen(
&mgr,
"http://127.0.0.1:8080",
fn,
NULL
);
The event loop is then kept alive with:
for (;;) {
mg_mgr_poll(&mgr, 1000);
}
The application can be opened at:
http://127.0.0.1:8080/
WebFormsJS can also be served directly by Mongoose.
C as a Web Programming Language
The complete stack is surprisingly small:
C
+
Mongoose
+
WebForms Core
+
WebFormsJS
+
HTML
There is no requirement for React, JSX, a frontend build system, or a separate frontend application.
The C application handles the server logic.
WebForms Core generates UI commands.
WebFormsJS executes those commands.
The browser remains the final place where the HTML DOM is manipulated.
Conclusion
Building an interactive web application in C does not require turning C into a frontend language.
Instead, C can remain the server-side language while WebForms Core provides a command-based mechanism for controlling the browser UI.
The architecture is:
C Server
↓
WebForms
↓
Commands
↓
WebFormsJS
↓
HTML DOM
WFC combines this architecture with compact commands, HTML-comment command embedding, queue management, serial request processing, Loader controls, and event debouncing.
The result is a server-oriented, stateless-by-design approach to interactive web UI, while still allowing applications to keep server-side state when a specific scenario requires it.
And yes, this entire example starts with a language that was originally designed decades ago for systems programming:
C.
Related links
In Elanat:
in GitHub:



Top comments (0)