What if a web application could provide rich, interactive user interfaces while keeping the server stateless, using standard HTTP, working naturally with RESTful architectures, and remaining based on HTML?
What if the server could define UI behavior without maintaining a server-side representation of the browser DOM?
This is the approach introduced by WebForms Core (WFC).
WebForms Core is a server-side UI manipulation technology developed by Elanat. It allows server-side code to generate commands that describe operations on the HTML DOM. These commands are then executed by WebFormsJS, the browser runtime of WFC.
The basic architecture is:
Server
↓
WebForms
↓
Commands
↓
WebFormsJS
↓
HTML DOM
With Elixir and Phoenix, this becomes:
Phoenix Controller
↓
WebForms
↓
Commands
↓
WebFormsJS
↓
HTML DOM
The important point is that the server does not need to keep a copy of the browser's UI state.
The browser owns the DOM.
The server generates commands.
WebFormsJS executes them.
A Stateless Approach to Server-Driven UI
Many interactive web architectures introduce some form of persistent UI state on the server.
WFC takes a different approach.
The server can process a request, generate the required UI commands, return the response, and finish processing the request.
Conceptually:
Request
↓
Server
↓
Generate Commands
↓
Response
↓
Server Request State Ends
The server does not need to maintain a continuously synchronized representation of the client's DOM.
This makes the architecture naturally compatible with stateless HTTP applications and horizontal scaling.
For example, multiple server instances can process independent requests:
┌── Server A
Browser ── HTTP ─┼── Server B
└── Server C
The application does not need a particular server instance to continuously own the browser's UI state.
HTML Is Still the Interface
WFC does not require a proprietary UI markup language.
The interface is HTML.
A normal Phoenix template can contain:
<form method="post" action="/">
<label for="txt_Name">Your Name</label>
<input name="txt_Name" id="txt_Name" type="text">
<input name="btn_SetBodyValue"
type="submit"
value="Click to send data">
</form>
There is no requirement to convert the page into a separate component language.
The HTML remains the HTML that the browser understands.
WebFormsJS operates on that existing DOM.
This creates a simple relationship:
HTML
+
WebFormsJS
+
Server Commands
=
Interactive UI
RESTful and HTTP-Friendly
A Server-Driven UI architecture does not necessarily require a persistent connection between the server and the browser.
WFC commands can be returned through ordinary HTTP responses.
For example:
POST /submit
↓
Phoenix Controller
↓
WebForms
↓
WebForms Core Response
↓
HTTP Response
↓
WebFormsJS
↓
DOM Update
This fits naturally into the request/response model of the web.
At the same time, WFC is not restricted to HTTP request/response communication.
Depending on the application's requirements, server-generated commands can also be transported through technologies such as:
- HTTP
- WebSocket
- Server-Sent Events (SSE)
The command architecture remains the same.
Only the transport mechanism changes.
┌── HTTP
WebForms Commands ├── WebSocket
└── SSE
↓
WebFormsJS
↓
HTML DOM
This separation between command generation and transport allows the UI architecture to remain independent from a single communication mechanism.
Server-Orchestrated UI
WFC can be described as a Server-Orchestrated UI architecture.
The server determines which UI operations should occur.
For example, server-side Elixir code can express:
form =
WebForms.new()
|> WebForms.set_font_size(InputPlace.tag("form"), 20)
|> WebForms.set_background_color(InputPlace.tag("form"), "#eeeeee")
|> WebForms.set_disabled(InputPlace.name("btn_SetBodyValue"), true)
The server is defining behavior.
The browser is executing behavior.
WebFormsJS receives the generated commands and applies them to the DOM.
The server does not need to send a complete replacement page.
It can send only the operations required for the current interaction.
The Commander–Executor Architecture
This creates a clear division between the server and the browser.
SERVER
│
▼
WebForms Class
Commander
│
▼
WebForms Commands
│
▼
BROWSER
│
▼
WebFormsJS
Executor
│
▼
HTML DOM
The WebForms class is the Commander.
It creates the instructions.
WebFormsJS is the Executor.
It interprets and executes the instructions.
This is different from sending application-level JavaScript from the server.
The server generates WebForms Core commands, while WebFormsJS provides the runtime that understands those commands.
Building a Phoenix Application
Let's see how this architecture works in a real Elixir application.
The example uses Phoenix and the wfc package.
Add WFC to the project's dependencies:
defp deps do
[
{:wfc, "~> 2.1"}
]
end
Then fetch the dependency:
mix deps.get
The package provides the WebFormsCore namespace:
alias WebFormsCore.{WebForms, InputPlace}
The Phoenix View
Create an index.html.heex view:
<script type="module" src="/script/web-forms.js"></script>
<form method="post" action="/">
<label for="txt_Name">Your Name</label>
<input name="txt_Name" id="txt_Name" type="text">
<br>
<label for="txt_FontSize">Set Font Size</label>
<input name="txt_FontSize"
id="txt_FontSize"
type="number"
value="16"
min="10"
max="36">
<br>
<label for="txt_BackgroundColor">Set Background Color</label>
<input name="txt_BackgroundColor"
id="txt_BackgroundColor"
type="text">
<br>
<input name="btn_SetBodyValue"
type="submit"
value="Click to send data">
</form>
The first GET / request renders the complete HTML page.
The browser now owns the interface.
When the user submits the form, Phoenix receives the form data.
The controller can then generate commands instead of rendering another complete page.
The Phoenix Controller
Create my_controller.ex:
defmodule MyAppWeb.MyController do
use MyAppWeb, :controller
alias WebFormsCore.{WebForms, InputPlace}
def index(conn, _params) do
render(conn, :index)
end
def submit(conn, %{
"txt_Name" => name,
"txt_BackgroundColor" => background_color,
"txt_FontSize" => font_size,
"btn_SetBodyValue" => _button
}) do
form =
WebForms.new()
|> WebForms.set_font_size(InputPlace.tag("form"), String.to_integer(font_size))
|> WebForms.set_background_color(InputPlace.tag("form"), background_color)
|> WebForms.set_disabled(InputPlace.name("btn_SetBodyValue"), true)
|> WebForms.add_tag(InputPlace.tag("form"), "h3", nil)
|> WebForms.set_text(InputPlace.tag("h3"), "Welcome #{name}!")
text(conn, WebForms.response(form))
end
end
The controller creates a WebForms object and builds the UI operations.
For example:
WebForms.set_font_size(
InputPlace.tag("form"),
String.to_integer(font_size)
)
changes the font size of the <form> element.
This:
WebForms.set_background_color(
InputPlace.tag("form"),
background_color
)
changes its background color.
And:
WebForms.set_disabled(
InputPlace.name("btn_SetBodyValue"),
true
)
disables the submit button.
The controller can also create new HTML elements:
WebForms.add_tag(
InputPlace.tag("form"),
"h3",
nil
)
and set their contents:
WebForms.set_text(
InputPlace.tag("h3"),
"Welcome #{name}!"
)
Finally:
text(conn, WebForms.response(form))
returns the generated WebForms Core response.
The response contains the commands that WebFormsJS will execute.
Phoenix Router
Add the routes:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
scope "/", MyAppWeb do
get "/", MyController, :index
post "/", MyController, :submit
end
end
The request flow is now:
GET /
↓
Phoenix
↓
HTML View
↓
Browser
After submitting the form:
POST /
↓
Phoenix Controller
↓
WebForms
↓
WebForms Core Commands
↓
HTTP Response
↓
WebFormsJS
↓
HTML DOM
No server-side DOM needs to be maintained.
WebFormsJS
WebFormsJS is the browser runtime responsible for executing WebForms Core commands.
Load it with:
<script type="module" src="/script/web-forms.js"></script>
Its role is not to replace HTML.
Its role is to operate on HTML.
The relationship is:
Server
↓
Command
↓
WebFormsJS
↓
Existing HTML DOM
This makes WebFormsJS an execution layer rather than a replacement for the browser's native document model.
Get WebFormsJS in GitHub: https://github.com/webforms-core/Web_forms
Get WebFormsJS in Elanat: https://elanat.net/webforms-js
Designed for Scale
Statelessness is particularly important for large web applications.
When UI state does not need to remain attached to a particular server process, requests can be distributed across multiple application instances.
┌── Phoenix Server 1
│
Browser ── Load Balancer ├── Phoenix Server 2
│
└── Phoenix Server 3
Each request can be processed independently.
The server receives the request, executes application logic, generates the required UI commands, returns the response, and completes the request.
This architecture can work naturally with common scaling mechanisms such as load balancing and multiple application instances.
The browser remains responsible for the DOM.
The server remains responsible for application logic and command generation.
Stateless Does Not Mean Less Interactive
A stateless server does not mean a static interface.
WFC commands can perform a wide range of DOM operations.
The server can instruct the browser to:
- Change text
- Change attributes
- Change styles
- Add elements
- Remove elements
- Enable or disable controls
- Replace content
- Move through UI flows
- Respond to user interactions
The interaction can therefore remain dynamic while the server remains stateless.
This is an important distinction:
Stateless Server
≠
Static UI
A server can be stateless while still orchestrating rich browser interactions.
One UI Architecture, Multiple Transports
Another important property of WFC is the separation between what should happen and how the command is transported.
The UI command itself can remain conceptually the same:
Set Text
Set Style
Add Element
Disable Button
Replace Content
The transport can vary:
HTTP
WebSocket
SSE
This makes it possible to select a communication model based on application requirements without changing the fundamental UI command architecture.
For a conventional form submission, HTTP may be sufficient.
For continuous server-to-browser events, SSE may be appropriate.
For bidirectional real-time communication, WebSocket may be appropriate.
The WebFormsJS runtime remains the execution environment.
HTML-Native Server-Driven UI
The web platform already provides a powerful document model: HTML and the DOM.
WFC builds its UI architecture around that model.
There is no requirement for a separate virtual DOM.
There is no requirement for a separate frontend project.
There is no requirement for a client-side component build pipeline.
The fundamental UI remains:
HTML → DOM
WebForms Core adds a server-generated command layer:
Server
↓
Commands
↓
WebFormsJS
↓
HTML DOM
This keeps the browser interface close to the native web platform while allowing the server to orchestrate interactive behavior.
A Different Way to Build Interactive Web Applications
WebForms Core combines several architectural ideas:
Stateless Server
+
RESTful / HTTP Architecture
+
HTML-Native UI
+
Server-Generated Commands
+
Browser Runtime
+
Multiple Transports
↓
Server-Orchestrated UI
For Elixir applications, Phoenix provides the server-side web framework, while WFC provides the server-side UI command layer and WebFormsJS provides the browser-side execution runtime.
The result is a model where:
The server owns the application logic.
The browser owns the DOM.
WebForms defines the UI commands.
WebFormsJS executes the commands.
And the server does not need to maintain a continuously synchronized copy of the browser interface.
Final Architecture
The complete architecture can be summarized in one diagram:
SERVER
│
Phoenix / Elixir
│
▼
WebForms Class
Commander
│
▼
WebForms Core Commands
│
┌──────────┼──────────┐
│ │ │
HTTP SSE WebSocket
│ │ │
└──────────┼──────────┘
│
▼
WebFormsJS
Executor
│
▼
HTML DOM
This is the core idea behind the new Server-Driven UI approach of WebForms Core:
The server orchestrates.
The browser executes.
HTML remains the interface.
The server remains stateless.
WebForms Core is a technology owned and developed by Elanat.
Official website: https://elanat.net
WebForms Core: https://elanat.net/webforms-core
WebForms Core GitHub: https://github.com/webforms-core

Top comments (0)