WebForms Core (WFC) is a web development technology owned and developed by Elanat. It provides a server-driven approach for building interactive web interfaces in which the server generates commands that are executed in the browser by WebFormsJS. WFC is designed to work independently of a specific web framework, so the Ruby implementation can be integrated with Sinatra, Rails, or other Ruby web frameworks that can handle HTTP requests and responses. The same architecture is also available for other backend languages, allowing WebForms Core to provide a consistent server-driven UI approach across different technology stacks.
WebForms Class and WebFormsJS
The Ruby implementation of WebForms Core consists of two main parts:
- WebForms Class: The server-side class used by the backend application to generate commands.
- WebFormsJS: The client-side JavaScript runtime that receives and executes those commands in the browser.
The WebForms class acts as the Commander. Your Ruby application creates a WebForms object and calls methods such as set_text, set_attribute, add_text, and set_comment_event.
WebFormsJS acts as the Executor. It runs in the browser and applies the generated operations to the existing DOM.
This means that the server does not need to send a complete new HTML page after every interaction. Instead, it can send a compact set of instructions describing what should change.
For example:
form.set_text("radio-name", Fetch.get_text("$"))
The server generates a command that tells WebFormsJS to update the element identified by radio-name. The browser then performs that operation on the existing page.
This approach also allows WebForms Core to work without a persistent server-side UI circuit or a virtual DOM. The browser maintains the actual HTML DOM, while the server generates commands when an interaction requires a change.
Building the Radio Player
The following example creates a simple radio player using Ruby, Sinatra, and WebForms Core.
The application retrieves radio station data, creates a radio card for each station, and assigns a click event to every generated card.
require 'sinatra'
require 'wfc'
include WebFormsCore
get '/' do
form = WebForms.new
form.not_exist(Fetch.cache("radio-data"))
form.add_cache_value("radio-data", Fetch.load_url("/api/radio.json"))
form.for_each("[0]", Fetch.cache("radio-data"), "foreach-data")
form.start_bracket
form.add_text("{radio-container}", Fetch.load_html("/api/template.html", "Radio"))
form.bind_json_to_template("{radio-card}-1", Fetch.format_store("foreach-data"), "[0]", "{{value}}")
form.set_comment_event("{radio-card}-1|<>", HtmlEvent.on_click, "play-radio")
form.end_bracket
form.start_index("play-radio")
form.set_attribute("audio", HtmlEvent.on_load_start, "this.play()")
form.set_attribute("audio", "src", Fetch.get_attribute("$", "data-stream"))
form.set_text("radio-name", Fetch.get_text("$"))
erb :view, locals: { form: form }
end
Loading the radio data
The first part of the code creates the WebForms object:
form = WebForms.new
The application then checks whether the radio data is already available in the client-side cache:
form.not_exist(Fetch.cache("radio-data"))
form.add_cache_value("radio-data", Fetch.load_url("/api/radio.json"))
If the cache does not contain radio-data, the data is loaded from /api/radio.json and stored in the cache.
radio.json
[
{
"title": "Radio Paradise",
"stream": "https://stream.radioparadise.com/aac-128"
},
{
"title": "SomaFM - Groove Salad",
"stream": "https://ice1.somafm.com/groovesalad-128-mp3"
},
{
"title": "SomaFM - Drone Zone",
"stream": "https://ice1.somafm.com/dronezone-128-mp3"
},
{
"title": "SomaFM - DEF CON Radio",
"stream": "https://ice1.somafm.com/defcon-128-mp3"
}
]
Creating the radio cards
The for_each operation iterates through the returned data:
form.for_each("[0]", Fetch.cache("radio-data"), "foreach-data")
The result of each iteration is then used inside the bracketed block:
form.start_bracket
form.add_text("{radio-container}", Fetch.load_html("/api/template.html", "Radio"))
form.bind_json_to_template("{radio-card}-1", Fetch.format_store("foreach-data"), "[0]", "{{value}}")
form.set_comment_event("{radio-card}-1|<>", HtmlEvent.on_click, "play-radio")
form.end_bracket
Here, load_html retrieves the HTML template for a radio station.
template.html
<!DOCTYPE html>
<template id="Radio">
<div class="radio-card">
<b data-stream="{{stream}}">
{{title}}
</b>
</div>
</template>
Note: Any call that targets a
<template>element returns only the structure inside it, not the<template>element itself.
bind_json_to_template fills the template with the current radio station's data.
Finally:
form.set_comment_event("{radio-card}-1|<>", HtmlEvent.on_click, "play-radio")
associates the click event with the play-radio action.
The important point is that these operations describe what should happen in the browser. Even the for_each method only generates a string that describes the operation. WebFormsJS executes the resulting commands on the client.
Handling the Play Action
The next section defines what happens when a radio card is clicked:
form.start_index("play-radio")
form.set_attribute("audio", HtmlEvent.on_load_start, "this.play()")
form.set_attribute("audio", "src", Fetch.get_attribute("$", "data-stream"))
form.set_text("radio-name", Fetch.get_text("$"))
start_index("play-radio") starts the command group associated with the event.
The selected radio card becomes the current element represented by $.
The stream URL is read from its data-stream attribute:
Fetch.get_attribute("$", "data-stream")
and assigned to the audio element:
form.set_attribute("audio", "src", Fetch.get_attribute("$", "data-stream"))
The radio name is then extracted from the selected element and displayed:
form.set_text("radio-name", Fetch.get_text("$"))
Finally, the loadstart event is configured to start playback:
form.set_attribute("audio", HtmlEvent.on_load_start, "this.play()")
The entire interaction is therefore defined from the server side without writing a separate JavaScript event handler for the radio cards.
HTML View
The Sinatra ERB view contains the normal HTML structure of the application:
<!DOCTYPE html>
<html>
<head>
<title>WebForms Core Radio</title>
<script type="module" src="/script/web-forms.js"></script>
<style>
...
</style>
</head>
<body>
<main>
<h1>Radio Stations in Ruby</h1>
<h2>Building a Radio Player with WebForms Core</h2>
<div class="radio-container"></div>
<audio id="audio" controls></audio>
<div id="now-playing">
<span class="playing-indicator">●</span>
<span class="speaker-icon">🔊</span>
<span id="radio-name">No radio selected</span>
</div>
</main>
<%= form.export_to_html_comment %>
</body>
</html>
The important part is:
<script type="module" src="/script/web-forms.js"></script>
This loads WebFormsJS, the client-side executor.
The other important part is:
<%= form.export_to_html_comment %>
The commands generated by the Ruby WebForms object are exported into the HTML response. WebFormsJS reads those commands and executes them in the browser.
The application therefore keeps its UI as standard HTML while WebForms Core provides the server-driven behavior.
Output of the export_to_html_comment method

Is the code above understandable to humans? Please share your thoughts in the comments section.
CSS
The CSS is completely independent of WebForms Core. You can use your own styles, CSS framework, or no framework at all.
For a compact version of the styles used in this example:
*{box-sizing:border-box}body{margin:0;min-height:100vh;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#101114;color:#fff}main{width:min(720px,calc(100% - 32px));margin:60px auto}h1{margin-bottom:24px;font-size:32px}.radio-container{display:grid;gap:12px}.radio-card{border:1px solid #2d3036;border-radius:14px;background:#181a1f;transition:.2s}.radio-card:hover{background:#22252b;border-color:#555a63;transform:translateY(-2px)}.radio-card b{display:block;font-size:18px;margin:0 24px;line-height:64px;cursor:pointer}audio{width:100%;margin-top:28px}#now-playing{margin:14px auto 0;padding:10px 16px;display:flex;align-items:center;justify-content:center;gap:9px;border-radius:10px;background:#181818;color:#f2f2f2;font-size:15px;font-weight:600;letter-spacing:.2px}.speaker-icon{font-size:18px}.playing-indicator{font-size:11px;animation:blink 1s infinite}@keyframes blink{0%,100%{opacity:1}50%{opacity:.2}}#radio-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
Installing WebForms Core for Ruby
WebForms Core is available as the wfc RubyGem.
Install it directly using the RubyGems CLI:
gem install wfc
Then require it in your Ruby application:
require "wfc"
include WebFormsCore
The
wfcGem provides theWebFormsCorenamespace and theWebFormsclass together with the WebForms Core APIs for Ruby.
WebFormsJS is installed separately because it is the client-side runtime:
CLI:
npm install webformsjs
WebFormsJS in npm: webformsjs on npm
If you cannot install WebFormsJS using npm, it is also available at the following link:
https://github.com/elanatframework/Web_forms
After installing the
webformsjspackage, configure your web server to serve itsweb-forms.jsfile at/script/web-forms.js, as used in the HTML view above.
Then include it in your HTML:
<script type="module" src="/script/web-forms.js"></script>
WebFormsJS is separate from the RubyGem and runs in the browser. This separation is intentional: the RubyGem provides the server-side Commander, while WebFormsJS provides the c*lient-side Executor*.
Conclusion
This radio player demonstrates how Ruby and WebForms Core can be used to build an interactive browser interface while keeping the application logic on the server.
The Ruby code describes the required UI operations through the WebForms class. WebFormsJS receives the resulting commands and applies them directly to the browser's DOM.
There is no need to build a separate client-side component system for this example. The HTML remains ordinary HTML, the server controls the interaction through WebForms Core, and the browser executes the resulting commands through WebFormsJS.
Source Code
WebForms Core:
https://github.com/webforms-core
WebForms Core classes:
https://github.com/webforms-core/Web_forms_classes
WebFormsJS:
https://github.com/elanatframework/Web_forms/blob/elanat_framework/web-forms.js



Top comments (0)