Premium Utility Styles, DAP Reactivity Fixes & Documentation Overhaul
pip install --upgrade dars-framework
We've significantly expanded the utility-first styling system to bring it closer to a better developer experience, adding many features:
bg-gradient-to-{dir}
,
from-{color}
,
via-{color}
, and
to-{color}
. Internally uses a modern CSS variable architecture (
--tw-gradient-stops
).
ring
,
ring-{n}
,
ring-{color}
,
ring-opacity-{n}
, and
ring-offset-{n}
.
text-
prefix is now intelligent. It automatically switches between
font-size
and
color
based on the provided value (e.g.,
text-xl
vs
text-indigo-500
).
divide-x
and
divide-y
to easily add borders between child elements.
accent-{color}
,
caret-{color}
,
line-clamp-{n}
, and expanded support for specific border sides (e.g.,
border-t-2
,
border-x-4
).
shadow-{color}
.
updateVRef
Reactivity
: Resolved a critical issue where components using
ValueRef
(via
setVRef
) were not consistently re-rendering when updated through Dars Action Protocol (DAP) scripts.
Secure Action Protocol (DAP) & Zero-Eval Runtime Hardening
pip install --upgrade dars-framework
new Function()
Building on the milestone of v1.8.9, v1.9.0 achieves a 100% "Zero-Eval" runtime for all dynamic event execution.
event
and
element
(the
this
context) without using
eval()
or
new Function()
.
Introduced a centralized
Command Registry
in the browser runtime (
dars.min.js
). This moves the framework from "sending code strings" to "sending structured commands".
dispatch(action, context)
function ensures that actions are processed as structured data objects
{op, args}
, eliminating the risk of arbitrary code execution.
The browser runtime now includes a comprehensive library of registered commands, covering almost all utility functions in
utils_ds.py
:
alert
,
confirm
(with DAP-driven
on_ok
/
on_cancel
callbacks), and
log
.
navigate
,
reload
,
history_back
,
history_forward
.
dom_show
,
dom_hide
,
dom_toggle
,
dom_focus
,
dom_blur
,
dom_reflow
.
dom_set_text
,
dom_set_html
(sanitized via DOMPurify),
dom_set_style
,
dom_set_attr
,
class_add
,
class_remove
,
class_toggle
.
storage_set
,
storage_remove
,
storage_clear
, and
storage_get
(with direct state-update mapping).
fetch
support with success and error handlers.
vref_update
and
vref_get
.
Fixed several long-standing issues with Prism.js integration and global asset management:
Native String Concatenation & Math Fixes
pip install --upgrade dars-framework
Fixed a major regression in
MathExpression
where using the
+
operator aggressively coerced all operands into
parseFloat()
. This behavior broke string concatenation, resulting in
0
or
NaN
when attempting to combine strings and
ValueRef
values.
(left + right)
.
.float()
or
.int()
on the
ValueRef
(e.g.
V(".num1").float() + V(".num2").float()
).
State V2 Reactivity Hardening & FunctionComponent Fixes
pip install --upgrade dars-framework
The Dars runtime state manager has been decoupled from strict physical DOM bindings, enabling robust reactivity for headless and virtual components:
change()
handler now persists new state values to the internal
__reactiveRegistry
before attempting to locate a DOM element. This ensures that virtual states update reliably even when no matching element ID exists.
increment
and
decrement
methods would always evaluate from
0
when bound to headless states. The
startLoop()
runtime function now checks the state registry if a target element is not found, allowing seamless mathematical operations in the background.
Resolved multiple issues affecting
useDynamic
and reactive bindings inside
@FunctionComponent
trees:
app.add_page
) generation. This ensures that all
useDynamic
bindings nested inside FunctionComponents are successfully collected and exported into the reactive Javascript bundle.
_generate_reactive_bindings_js
) that caused silent failures (missing closing braces) when exporting a project containing exclusively FunctionComponent bindings without any standard built-in bindings.
Ultimate Security & Reactivity Hardening: Removal of Eval/New Function & Native JS Compilation
[!IMPORTANT] SECURITY ADVISORY : v1.8.9 achieves a major milestone by removing
eval()andnew Function()from the core client-side runtime (dars.min.js). However, the web framework (as seen in certain SSR/Fullstack exports) is not yet 100% free ofnew Function()and_executeExternalScriptfor specific dynamic execution flows. This will be fully addressed in the upcoming Dars Flight Protocol (DFP) release.
pip install --upgrade dars-framework
We have completely overhauled how Dars executes dynamic code in the browser.
eval()
and
new Function()
have been eliminated from the runtime (
dars.min.js
).
await
Support
: You can now use
await
directly within any event handler or transformation script.
The
dScript
compiler is now a core framework utility, moving complex resolution logic from the browser to the build/export phase.
V()
,
MathExpression
, and
BooleanExpression
are now compiled into clean, native JavaScript code strings.
compile_val
logic to ensure that complex structures (lists, dicts) containing reactive objects are correctly translated into executable JS literals, resolving previous "RawJS is not serializable" warnings.
Fixed several long-standing issues with the reactivity pipeline:
NaN
errors in calculators. The compiler now correctly handles the
+
operator, favoring native JS concatenation for strings and addition for numbers.
url()
and
transform()
helpers to use a structured concatenation model, eliminating
SyntaxError: Unexpected identifier
issues caused by nested backticks in template literals.
The initial rendering engine (
_elFromVNode
) is now asynchronous-aware:
compile_val
now recursively handles nested collections, ensuring all parts of a complex prop are correctly compiled.
ValueRef
string representation to integrate seamlessly with the new native compiler.
Critical Security Update: Dars Server Protocol (DSP) & SSR Hydration Fix
[!CAUTION] SECURITY WARNING : Versions <= v1.8.7 are considered deprecated and NOT recommended for production use. v1.8.8 addresses critical security surfaces by temporarily removing experimental Server Components. Upgrading is mandatory.
pip install --upgrade dars-framework
Introduced a new unified protocol for transmitting VDOM snapshots, component states, and reactive bindings from the server to the client. This ensures that SSR-rendered pages are hydrated with full parity to client-side renders.
Resolved critical issues where reactive bindings (
useDynamic
) and
VRef
bindings were not correctly executed after initial server rendering.
As part of security hardening, the experimental "Dars Server Components" feature (using
use_server=True
) has been removed from this version.
Dars Server Components & FastAPI Integration
pip install --upgrade dars-framework
v1.8.7 introduces first-class Server Components , allowing individual components to be fully rendered on the server while maintaining client-side interactivity.
use_server=True
to any component inheriting from the base
Component
class.
Button("Server Rendered Button", use_server=True, on_click=...)
The new version of
create_dars_app
plugin provides tight integration with FastAPI, making it easier than ever to build full-stack SSR applications.
Scaffold a complete SSR project and then add Server Components support in seconds:
dars init my-app --type ssr
This template sets up:
create_dars_app
.
Environment Management & File Upload Component
pip install --upgrade dars-framework
New
DarsEnv
class provides a standard way to check the current environment mode:
DarsEnv.dev
: returns
True
during development (
dars dev
), and
False
during production builds (
dars build
/
dars export
).
This allows you to write conditional logic in your components:
from dars.env import DarsEnv
Link(target="/docs" if DarsEnv.dev else "https://example.domain.com/env", text="Docs")
A new
FileUpload
component is now available in
dars.components.advanced
:
<input type="file">
with a custom, styleable interface.
accept
,
multiple
, and hidden input handling.
on_change
events.
from dars.components.advanced import FileUpload
FileUpload(
label="Upload Document",
accept=".pdf",
on_change=log("File uploaded")
)
Outlet improvements + SSR lazy-load placeholders + SPA router hardening
pip install --upgrade dars-framework
outlet_id
Nested routing now supports targeting a specific outlet in a parent layout:
Outlet(outlet_id="main" | "sidebar" | ...)
app.add_page(..., outlet_id="...")
outletId
per route so the client router can mount the child route into the correct outlet.
Outlet(placeholder=...)
Outlet
can render an optional placeholder while the child route region is empty (e.g. SSR lazy-load or SPA navigation).
If
placeholder
is not provided, the outlet remains empty.
New API:
app.set_loading_state(loadingComp, onErrorComp)
Exporters and the SSR backend render these as static HTML placeholders and expose them to the SPA router. This keeps state/events safe and avoids breaking hydration.
The SPA router now treats paths with trailing slashes as equivalent:
/dashboard
and
/dashboard/
match the same route
This prevents incorrect 404 redirects when a user navigates to a valid route with a trailing slash.
Python-native minification + major Utility Styles upgrade
pip install --upgrade dars-framework
v1.8.4 upgrades the default minification pipeline to use real, battle-tested Python minifiers:
rjsmin
rcssmin
This makes builds and exports work reliably in pure-Python environments.
viteMinify
mode preserved
If you enable
viteMinify: true
in
dars.config.json
, Dars can still use Vite/esbuild
optionally
when available.
When tools are not installed, Dars falls back to the Python minifiers automatically.
dars.min.js
The embedded runtime bundle (
dars.min.js
) is now treated as a special case:
export
output).
rjsmin
, regardless of
viteMinify
.
prop-[value]
)
The utility system now supports Tailwind-like arbitrary properties :
style="background-image-[linear-gradient(90deg,_rgba(0,0,0,.35),_#00ffcc)]"
style="padding-[calc(1rem_+_2vw)]"
style="color-[var(--brand-color)]"
style="--brand-color-[#00ffcc]"
Also includes background gradient support via
bg-[linear-gradient(...)]
(maps to
background-image
).
filter
/
backdrop-filter
/
transform
Multiple filter/transform utilities now compose instead of overwriting:
style="filter-[blur(6px)] filter-[brightness(120%)]"
text-[#hex]
/
text-[rgba(...)]
being interpreted as
font-size
instead of
color
.
border-top-[...]
incorrectly becoming
border-color: top-[...]
.
The LandingPage navbar now uses
style="..."
utility strings instead of large inline style dicts,
improving consistency and providing a real-world example of the upgraded styling system.
Critical Build Fix
pip install --upgrade dars-framework
Unexpected token 'export'
In some environments, the JS minification pipeline (Vite/esbuild) could emit ESM output ending with
export default ...
inside
app.js
. Since exported pages load
app.js
as a classic script, browsers would fail to parse it with:
Uncaught SyntaxError: Unexpected token 'export'
v1.8.3 fixes this by forcing the minifier output format to
IIFE
for browser scripts, preventing ESM
export
statements from being generated during build.
Bug Fixes & Utility System Improvements
pip install --upgrade dars-framework
The
setTimeout
utility function in
utils_ds.py
has been updated to properly return a Promise, enabling correct chaining with
.then()
operations. This fixes JavaScript syntax errors that occurred when using sequential animations or delayed operations.
Improved the animation chaining system to handle missing DOM elements gracefully, preventing runtime errors when referenced elements don't exist in the component tree.
Updated CSS media queries for better handling of text overflow on small screens, ensuring content remains readable across all device sizes without cutting off important information.
Style System Optimization & SSR-Aware Registry
pip install --upgrade dars-framework
v1.8.1 introduces the first phase of a new style optimization system focused on reducing inline CSS while keeping full compatibility with Dars reactivity and dynamic operations.
style={...}
or Tailwind-like strings in
style="..."
are now:
.dars-s-<hash>
.
style
blocks.
The original
class_name
remains fully respected and is appended
after
the generated
dars-s-*
class, so user classes (and external CSS frameworks) retain override power.
The optimized styles are accumulated into a central registry and injected in the
<head>
as:
<link rel="stylesheet" href="runtime_css.css" />
<style id="dars-style-registry">
/* .dars-s-* rules here */
</style>
<link rel="stylesheet" href="styles.css" />
Order is carefully chosen so that:
runtime_css.css
provides the base UI tokens and default component styling.
#dars-style-registry
contains all extracted
.dars-s-*
rules (including those coming from
hover_style
/
active_style
phases on future releases).
styles.css
(hover/active styles +
app.add_global_style()
+ user CSS files) comes last, ensuring user styles can override the framework-generated ones.
The new style pipeline now runs consistently across all export modes:
Single page & multipage :
HTMLCSSJSExporter.export
collects static styles from the component tree before rendering.
#dars-style-registry
block in the head.
SPA export (
_export_spa
)
:
html
.
__DARS_SPA_CONFIG__
now includes a
styles
field containing the CSS for that route.
_injectStyles(routeName, styles)
so that SSR/SPA navigations share the same optimized classes.
SSR backend (
dars.backend.ssr
)
:
SSRRenderer.render_route
uses a
deep copy
of each route's root tree to avoid mutating the original components when collecting styles.
.dars-s-*
classes, and the resulting CSS is injected into the SSR HTML head using
#dars-style-registry
.
/api/ssr/<route>
) now returns a
styles
field alongside
html
,
vdom
,
events
, etc., so the SPA router can inject the same registry CSS on client-side navigations.
The embedded JS runtime (
dars/js_lib.py
→
DARS_MIN_JS
) has been updated to be style-optimization aware without breaking existing behavior:
Dars.change({ id, dynamic: true, style: {...} })
and state rules that manipulate
attrs.style
continue to write directly to
el.style[...]
.
style
attribute, so elements whose base styles were moved to
.dars-s-*
remain fully reactive.
attrs.class
preserve internal
dars-*
classes (including
.dars-s-*
) and only replace user classes, ensuring the optimization never gets wiped by state changes.
/api/ssr/...
and now respects the
styles
payload from the backend.
_injectStyles(routeName, styles)
on every SSR navigation so that optimized classes stay active even after client-side route changes.
These changes are designed to be
backwards compatible
for projects that used only
style
/
class_name
and dynamic state. The main effect you will notice in v1.8.1 is smaller, cleaner HTML with fewer repeated inline styles, especially for static or Tailwind-like styling.
Advanced Multimedia Components & Electron Security Baseline
pip install --upgrade dars-framework
v1.8.0 introduces two new first-class components in the basic library:
Video
: wrapper around
<video>
Audio
: wrapper around
<audio>
Both are fully reactive and integrate with the existing hooks system:
State
+
useDynamic
for:
src
autoplay
muted
loop
controls
plays_inline
(Video)
useValue
for non-reactive initial values.
VRef
(setVRef/useVRef) can target
src
and other props when needed.
Example:
from dars.all import *
from dars.hooks.value_helpers import V
media_state = State(
"media",
current_video="/media/intro.mp4",
current_audio="/media/theme1.mp3",
autoplay_video=False,
muted_video=True,
loop_audio=True,
)
@route("/", index=True)
def index():
return Page(
Container(
Video(
src=useDynamic("media.current_video"),
poster="/media/poster.jpg",
width="720",
controls=True,
autoplay=useDynamic("media.autoplay_video"),
muted=useDynamic("media.muted_video"),
preload="metadata",
),
Button(
"Toggle Mute",
on_click=media_state.muted_video.set(
(V("media.muted_video").bool() == True).then(False, True)
),
),
Button(
"Toggle Autoplay",
on_click=media_state.autoplay_video.set(
(V("media.autoplay_video").bool() == True).then(False, True)
),
),
Text("Audio actual:"),
Text(useDynamic("media.current_audio")),
Audio(
src=useDynamic("media.current_audio"),
controls=True,
loop=useDynamic("media.loop_audio"),
preload="auto",
),
)
)
The web exporter has been extended so that
useDynamic
bindings on boolean attributes behave correctly:
State
value changes, the runtime:
autoplay
,
muted
,
loop
,
controls
,
playsinline
.
el.autoplay
,
el.muted
, etc.).
useDynamic
) no longer count as _truthy_ defaults:
controls=True
,
plays_inline=True
remain active by default.
autoplay
,
loop
,
muted
are off by default unless explicitly set by state or by a literal
True
.
This ensures that Video/Audio behave predictably both on initial render and during reactive updates.
The HTML/CSS/JS exporter now supports a convention-based media folder :
media/
directory, Dars will:
media/
into the export
output_path
.
src="/media/..."
used in
Image
,
Video
or
Audio
will point to real files in the exported build.
This makes it straightforward to ship videos, audio tracks and posters alongside your static export.
To keep desktop builds secure and reproducible, v1.8.0 introduces an Electron security baseline :
39.2.6
:
dars/templates/desktop/template/backend/package.json
init
desktop scaffolds and
init --update
flows.
dars doctor
gains version-awareness:
MIN_SAFE_ELECTRON = "39.2.6"
.
dars doctor --all --yes
, Dars will install/update Electron globally via Bun as
electron@39.2.6
and
electron-builder@latest
.
dars dev
for desktop projects now warns if your installed Electron is below the baseline and suggests:
dars doctor --all --yes
This keeps both templates and global tooling aligned with a reviewed Electron version.
Several quality-of-life fixes improve desktop (Electron) development:
App.rTimeCompile
desktop branch:
dars core/js_bridge
:
electron_dev_spawn
now sets
ELECTRON_DISABLE_SECURITY_WARNINGS=true
for dev runs.
Result: smoother desktop dev cycle with clear logs and no accidental web preview server when working on Electron apps.
LandingPage/documentation/markdown/components.md
now documents:
Video
and
Audio
components.
State
,
useDynamic
,
V()
and VRefs.
media/
folder convention for assets.
Global JS Error Handling & safer eval() in the runtime
pip install --upgrade dars-framework
This release introduces a
centralized eval helper
in the core Dars runtime (
dars.min.js
), designed to make hydration/runtime debugging safer and more observable without changing existing behavior in production:
New internal helper in the JS runtime:
function _safeEval(code, ctx) {
if (code == null) return;
try {
return (0, eval)(code);
} catch (err) {
console.error("[Dars] Eval error:", err);
// Optional dev hook – only called if you define it
try {
const D =
(globalThis && globalThis.Dars) ||
(typeof window !== "undefined" ? window.Dars : null);
if (D && typeof D.onError === "function") {
D.onError(err, Object.assign({ code: String(code) }, ctx || {}));
}
} catch (_) {}
}
}
The runtime keeps using
eval
internally where necessary, but now all critical call sites go through
_safeEval
instead of raw
(0,eval)(...)
.
To help you surface client-side errors during development (especially hydration and dynamic updates), the runtime now exposes a single global hook :
window.Dars.onError(err, context)
is invoked by
_safeEval
whenever a runtime-generated script fails.
context
is a small dictionary that always includes:
code
: the JS string that was evaluated.
window.Dars
or
window.Dars.onError
are not defined, the runtime simply logs to
console.error
and continues as before.
Two of the most important dynamic execution points are now wired to
_safeEval
and the global hook:
Event handlers attached from the VDOM
(0,eval)(c)
inside
try/catch
blocks.
_safeEval(c, { type, event, phase: 'event_handler' })
.
window.Dars.onError
, you receive:
Error
instance.
event
).
type
) and phase (
'event_handler'
).
Component lifecycle hooks (onMount, onUpdate, onUnmount)
try/catch
.
_runLifecycle(id, hook)
delegates to
_safeEval(code, { hook, id })
when a lifecycle snippet is present.
Result: you get a single, consistent error-reporting channel for all dynamic runtime code, without changing how the framework behaves when no hook is provided.
dars/js_lib.py
_safeEval(code, ctx)
helper to the embedded
DARS_MIN_JS
runtime.
_safeEval
and forward enriched context to an optional
window.Dars.onError
hook.
Full-Stack SSR DX: backendEntry Defaults, Validation & CLI Backend Runner
pip install --upgrade dars-framework
When you scaffold a new SSR project with:
dars init my-app --type ssr
Dars now creates a
dars.config.json
that includes a default backend entry:
"backendEntry": "backend.api:app"
This matches the generated
backend/api.py
file and makes it trivial to start the FastAPI SSR backend.
The
dars config validate
command has been extended to understand when your project is using SSR:
entry
module and inspects:
app._spa_routes
) for
RouteType.SSR
.
app._pages
) whose root metadata has
route_type = RouteType.SSR
.
RouteType.SSR
in the entry source file.
backendEntry
is missing in
dars.config.json
, validation now reports:
This app is not going to work because you don't have configured the backendEntry in dars.config.json with your backend entry file.
This prevents half-configured SSR apps where routes are marked as
RouteType.SSR
but no backend has been wired.
dars dev --backend
To simplify the SSR development workflow, a new flag was added to the
dars dev
command:
# Frontend dev (preview + hot reload)
dars dev
# Backend-only dev (SSR/API)
dars dev --backend
Behavior:
dars dev
(no flags):
entry
from
dars.config.json
(default
main.py
).
python main.py
), which is expected to call
app.rTimeCompile()
.
http://localhost:8000
with hot reload.
dars dev --backend
:
backendEntry
from
dars.config.json
(e.g.
"backend.api:app"
).
Starts the FastAPI SSR backend via uvicorn:
python -m uvicorn backend.api:app --reload --host 127.0.0.1 --port 3000
If
backendEntry
is missing, uses the same validation message as
dars config validate
to explain that SSR will not work.
Result: you can now run a full-stack SSR dev setup with two explicit terminals:
# Terminal 1 - Frontend
dars dev
# Terminal 2 - Backend
dars dev --backend
This keeps logs cleanly separated while still leveraging the built-in hot reload from
app.rTimeCompile()
and uvicorn's reload system.
Component Lifecycle Hooks, Dynamic VRefs & Safer Runtime Updates
pip install --upgrade dars-framework
This release introduces
component-level lifecycle hooks
, available both for regular components and
@FunctionComponent
:
onMount
onUpdate
onUnmount
dScript
& functions or inline JS strings.
lifecycle
block per node with:
inline
/
dscript
).
The dynamic backend helpers are now fully compatible with lifecycle, events and VRefs:
createComp(target, root, position="append")
:
VDomBuilder
to serialize the full component into VDOM.
_events
map produced by the exporter for that subtree.
runtime.createComponent(rootId, vdom, pos)
:
_attachEventsForVNode
).
__lifecycle
and fires
onMount
for all nodes with hooks.
DarsHydrate(el)
so the rest of bindings (V(), VRefs, useDynamic, etc.) attach to the dynamic subtree as well.
deleteComp(id)
:
runtime.deleteComponent(id)
.
onUnmount
for that id before removing the node and its VDOM entry.
Result: components created and destroyed with
createComp
/
deleteComp
have the
same lifecycle behavior
and event rehydration as components rendered in the initial VDOM.
The
onUpdate
hook now fires in two key scenarios:
Dynamic state changes
(runtime
change()
):
Dars.change({ id, dynamic: true, ... })
is called (e.g. via
updateComp()
), the runtime:
text
,
html
,
style
,
attrs
,
classes
, etc.
watch()
/ internals).
_runLifecycle(id, 'onUpdate')
whether the element exists or the change is purely logical.
VRef changes
via
updateVRef()
:
Dars.updateVRef(selector)
.
The JS generated by
updateVRef(...)
now, after updating the DOM and
window.__DARS_VREF_VALUES__
, does:
if (window.Dars && window.Dars.updateVRef) {
window.Dars.updateVRef(selector);
}
Dars.updateVRef(selector)
:
onUpdate
for that id (once per id), allowing parent components to react to shared VRef changes.
Several adjustments ensure
setVRef()
works equally well in the initial render and in components created dynamically via
createComp
:
setVRef(value, selector)
now creates a
VRefValue
whose metadata is preserved in the VDOM:
Text(text=setVRef(0, ".dyn_count"))
serializes as:
text: "0"
(initial value).
class: "dyn_count"
auto-added when the selector is of type
.class
.
window.__DARS_VREF_VALUES__['.dyn_count'] = 0
during hydration.
Text
is part of a component created with
createComp
: the VDOM includes both the initial value and the class, and
updateVRef(".dyn_count", ...)
can find and update all instances correctly.
To make it easier to compose numeric and literal expressions with
V()
, this release adds the
equal()
helper in
dars.hooks.value_helpers
:
from dars.hooks.value_helpers import V, equal
# Sumar 1 a un VRef
Button(
"Increment",
on_click=updateVRef(".dyn_count", V(".dyn_count").int() + 1),
)
# Resetear a 0 usando equal()
Button(
"Reset",
on_click=updateVRef(".dyn_count", equal(0)),
)
# Combinar con otra expresión basada en V()
expr = V(".a").int() + equal(V(".b").int())
updateVRef(".result", expr)
equal(value)
normalizes any value (literal,
V()
,
MathExpression
, etc.) into a
MathExpression
, so it integrates into the operation tree with the same async semantics and
NaN
validation as the rest of the expression system.
Note: to reset a VRef it's better to use
equal(0)directly as the target value instead of adding it to the current value.
JS generation for
updateComp()
(dynamic backend) has been hardened so it works correctly with complex expressions containing
//
comments and async IIFEs such as those generated by
updateVRef(...)
:
updateComp()
now:
//
comments consume the rest of the line.
;
from
dScript.code
before inlining it into the
change({ ... })
payload.
updateComp()
to be combined with complex scripts (including those generated by
V()
/
updateVRef
) without causing
SyntaxError
in
app.js
.
The hooks and components documentation has been updated to reflect these changes:
LandingPage/documentation/markdown/hooks.md
:
equal()
helper and examples together with
V()
and
updateVRef
.
LandingPage/documentation/markdown/components.md
:
onMount
/
onUpdate
/
onUnmount
).
Security Hardening for Prism.js Integration
pip install --upgrade dars-framework
This release focuses on tightening the security of the Prism.js integration used for code highlighting in exported documentation and apps.
Previously, the Prism script was injected without any additional security attributes:
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
Starting from
v1.7.6
, Dars now injects the script tag with
Subresource Integrity (SRI)
,
crossorigin
, and
referrerpolicy
attributes:
<script
src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"
integrity="sha512-7Z9J3l1+EYfeaPKcGXu3MS/7T+w19WtKQY/n+xzmw4hZhJ9tyYmcUS+4QqAlzhicE5LAfMQSF3iFTK9bQdTxXg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
></script>
crossorigin="anonymous"
is required for SRI on cross-origin resources and avoids leaking credentials.
referrerpolicy="no-referrer"
prevents the browser from sending the full URL of your site to the CDN as the HTTP referrer.
This change makes the default code-highlighting setup more secure by default, while remaining fully backwards compatible for existing projects.
CLI Doctor, Error Handling & JS Minification Improvements
pip install --upgrade dars-framework
dars doctor
The
dars doctor
command has been refined to give you
actionable diagnostics
with less noise:
esbuild
,
vite
,
electron
,
electron-builder
pyproject.toml
dars doctor --check
exits with code
0
when core requirements (Python deps) are satisfied,
1
otherwise.
dars doctor --all --yes
attempts to install all missing tools using Bun and pip without extra prompts.
powershell -NoProfile -ExecutionPolicy Bypass -c "irm bun.sh/install.ps1 | iex"
curl -fsSL https://bun.sh/install | bash
bun add -g vite
bun add -g esbuild
bun add -g electron
bun add -g electron-builder
These improvements make
dars doctor
a practical environment bootstrapper, not just a checker.
The CLI now avoids dumping huge tracebacks for common scenarios:
main()
entrypoint shows concise messages:
KeyboardInterrupt
(Ctrl+C):
Process interrupted by user
.
Process failed: <message>
.
DARS_DEBUG=1
.
dars build
specifically catches
KeyboardInterrupt
around the heavy export step and reports:
Process interrupted by user during build
instead of a long stack trace.
This improves the day-to-day UX, especially when cancelling long builds or dev sessions.
app.js
in Build Mode
The web exporter has been updated so that JS combination depends only on the bundle flag , not on Vite:
bundle=True
(build/production):
app.js
bundle containing:
app_{slug}.js
.
bundle=False
(development):
runtime_dars*.js
,
script*.js
,
vdom_tree*.js
) for easier debugging.
This guarantees consistent bundling in production regardless of whether Vite is used for minification.
JS minification has been hardened to avoid runtime breakage and to clearly separate default minify from Vite-based minify:
minify_js
now follows this strategy:
viteMinify
is enabled (
DARS_VITE_MINIFY=1
) and Vite is available, use Vite for JS minification.
esbuild
is installed, fall back to
esbuild
as the minifier backend.
viteMinify
is disabled, skip Vite/esbuild and use the conservative
regex-based Python minifier
(comments + whitespace) for JS.
dars.min.js
is now
explicitly skipped
by the default minifier to avoid corrupting already-minified code.
SyntaxError: Invalid or unexpected token
errors that could occur when re-minifying the runtime.
Applying minification (default)
when only the Python-side minifier is used.
Applying minification (default + vite)
when both the default and Vite/esbuild pipelines are active.
These changes make minification more predictable, safer for the Dars runtime, and better aligned with the
viteMinify
flag in
dars.config.json
.
Real SSR Hydration, SPA Config & Streaming
pip install --upgrade dars-framework
Dars Framework 1.7.4 refines the SSR system to deliver a true SSR experience :
<head>
+
<body>
) using
SSRRenderer
.
__dars_spa_root__
, allowing the Dars SPA router to hydrate the existing DOM without re-rendering from scratch.
window.__DARS_SPA_CONFIG__
, so the client runtime knows all paths, route types, and SSR endpoints from the very first load.
To enable full hydration without a static export pipeline, the SSR backend now serializes the initial state:
window.__DARS_STATE__
window.__DARS_STATE_V2__
(via
State.to_dict()
)
The
dars.min.js
runtime automatically registers the V1 snapshot and exposes the V2 snapshot as
Dars.stateV2Snapshot
for tools and advanced debugging.
The SSR JSON endpoints (
/api/ssr/{route_name}
) now return richer information for the SPA router and external tooling:
html
– Body HTML for incremental hydration.
vdom
– Per-route VDOM snapshot.
events
– Event map for attaching handlers on the client.
states
– V1 state snapshot.
statesV2
– V2 state snapshot.
spaConfig
– Minimal SPA routes configuration.
headMetadata
– Metadata extracted from the
Head
component.
create_ssr_app
now accepts a new optional
streaming
parameter:
from dars.backend.ssr import create_ssr_app
fastapi_app = create_ssr_app(dars_app, streaming=True)
When
streaming=True
, HTML responses for SSR routes are sent in two parts:
<!DOCTYPE html>
,
<html>
, full
<head>
and the opening
<body>
tag.
__dars_spa_root__
+ hydration scripts).
This allows browsers and crawlers to receive metadata and the
<head>
as early as possible
, improving FCP and SEO while the body content finishes rendering.
Complete Head Component & SEO Metadata Support
pip install --upgrade dars-framework
The
Head
component now works seamlessly across all export modes:
SPA
,
SSR
, and
Multipage
. You can manage your page title, description, and social media metadata (Open Graph, Twitter Cards) directly from your Python components.
Head(
title="My App",
description="The best app ever",
og_image="/share.png"
)
In Single Page Applications (SPA), the document metadata now updates automatically when navigating between routes. The router intelligentally applies:
<title>
description
,
keywords
, etc.)
og:title
,
og:image
, etc.)
This ensures your SPA is SEO-friendly and displays correct previews when shared on social media, even for client-side routes.
Performance & Developer Experience Enhancements
pip install --upgrade dars-framework
The
dars dev
command now exits instantly when pressing Ctrl+C. All cleanup operations (watchers, server, file deletion) happen in background threads, providing immediate feedback without blocking.
Exit Flow:
Ctrl+C pressed
↓
"Preview stopped" (instant)
↓
"Cleaning up preview files..." (spinner, max 2s)
↓
"Preview files deleted" (done)
Hot module replacement is now significantly faster through intelligent caching and change detection.
Optimizations Applied:
The framework library is no longer rewritten on every hot reload if the content hasn't changed.
# Check if file exists and content matches
if os.path.exists(dest_js):
with open(dest_js, 'r') as f:
if f.read() == DARS_MIN_JS:
# Skip write - already up to date
return
Static assets in
public/
or
assets/
directories are only copied when changes are detected.
Change Detection:
Marker File (
.public_sync
):
1733544800.123 # last sync timestamp
42 # file count
Development Mode (bundle=False):
.public_sync
marker for optimization
Production Mode (bundle=True):
File watchers now initialize in a background thread, allowing the preview server to start immediately.
Before (v1.7.1):
After (v1.7.2):
All performance improvements from Web mode have been applied to Desktop mode (Electron) for consistent behavior.
Improvements:
All blocking operations moved to daemon threads:
def _background_cleanup():
# Stop watchers
for w in watchers:
w.stop()
# Stop directory watchers
for dw in directory_watchers:
dw.stop()
# Server shutdown
if server:
server.httpd.shutdown()
server.httpd.server_close()
# Non-blocking execution
cleanup_thread = threading.Thread(target=_background_cleanup, daemon=True)
cleanup_thread.start()
Change detection algorithm:
# Read marker
with open('.public_sync', 'r') as f:
last_sync, last_count = f.read().split('\n')
# Check for changes
current_count = 0
for root, dirs, files in os.walk(public_abs):
for file in files:
current_count += 1
if os.path.getmtime(file) > last_sync:
return True # Modified or new file
# Check for deletions
if current_count != last_count:
return True # Files added or removed
return False # No changes
dars/core/app.py
- Background cleanup, watcher initialization
dars/exporters/web/html_css_js.py
- Smart caching, change detection
All existing projects will automatically benefit from:
dars dev
startup
Note:
Development mode will create a
.public_sync
file in the preview directory. This file is used for optimization and can be safely ignored. It is not created in production builds.
CLI Performance & Developer Experience Improvements
pip install --upgrade dars-framework
The
dars dev
command now starts
instantly
thanks to background directory cleanup. No more waiting for file deletion(probably)!
Before (v1.7.0):
dars_preview
directory deletion
After (v1.7.1):
How it works:
dars_preview
renamed to
dars_preview_trash_{timestamp}
Pressing Ctrl+C now exits immediately with visual feedback.
Before (v1.7.0):
After (v1.7.1):
Exit Flow:
Ctrl+C pressed
↓
"Exiting server..." (immediate)
↓
"Cleaning up preview files..." (spinner, max 2s)
↓
"✔ Preview files deleted." (done!)
Problem: After the first load, code blocks had Prism.js highlighting. But after making a change and triggering hot reload, the highlighting disappeared.
Root Cause:
The
HTMLCSSJSExporter
instance is reused across hot reloads. State flags like
_hljs_injected_pages
persisted, causing the exporter to skip script injection on subsequent exports.
Solution:
Reset state flags at the start of every
export()
call:
# dars/exporters/web/html_css_js.py
def export(self, app, output_path, bundle=False):
# Reset state for HMR
if hasattr(self, "_hljs_injected_pages"):
self._hljs_injected_pages.clear()
# Reset lazy script flags
lazy_keys = [k for k in self.__dict__.keys()
if k.startswith("_lazy_script_injected_")]
for k in lazy_keys:
delattr(self, k)
Result: Syntax highlighting now works reliably across all hot reloads!
Problem: Code blocks appeared as black boxes with no content.
Root Cause:
A regex pattern in
render_markdown
was capturing the closing
>
of the
<code>
tag, removing all content after it.
Solution: Fixed regex to preserve code content:
# Before (broken)
re.sub(r'<pre([^>]*)>\s*<code(?![^>]*class=)([^>]*)>', ...)
# After (fixed)
re.sub(r'<pre([^>]*)>\s*<code(?![^>]*class=)', ...)
Visual Feedback:
# Rename old directory
trash_name = f"dars_preview_trash_{int(time.time()*1000)}"
os.rename(preview_dir, trash_path)
# Delete in background thread
def _bg_cleanup(path):
shutil.rmtree(path, ignore_errors=True)
threading.Thread(target=_bg_cleanup, args=(trash_path,), daemon=True).start()
The HMR fix introduces a pattern for managing exporter state across hot reloads:
# Reset per-page injection tracking
if hasattr(self, "_hljs_injected_pages"):
self._hljs_injected_pages.clear()
# Reset dynamic flags
lazy_keys = [k for k in self.__dict__.keys()
if k.startswith("_lazy_script_injected_")]
for k in lazy_keys:
delattr(self, k)
This ensures scripts are re-injected on every export, maintaining consistency across development cycles.
dars/core/app.py
- Background cleanup, startup/exit spinners
dars/exporters/web/html_css_js.py
- HMR state reset, regex fix
Complete VRef System & Multi-Element Updates
pip install --upgrade dars-framework
Introducing
setVRef()
, a powerful new hook that allows you to create independent value references tied to specific DOM selectors. Unlike
useValue
which sets initial values for single components,
setVRef
creates a lightweight, portable reference that can be shared across your application.
Key Capabilities:
.shared-value
) to share a single value across multiple components.
@FunctionComponent
templates.
The
updateVRef()
hook has been upgraded to support widespread updates. When you use a
class selector
(e.g.,
.my-value
) with
setVRef
and
updateVRef
,
all
matching elements in the DOM will update simultaneously.
This allows you to share a single "value reference" across multiple independent components (like a main display and a sidebar preview) and keep them in sync with a single line of code.
Extended
useValue
and
useDynamic
support to cover the entire component library. You can now use reactive bindings in:
Card
Modal
Navbar
DatePicker
ProgressBar
Tooltip
Spinner
from dars.all import *
# 1. Define a shared reference using a class selector
count_ref = setVRef(0, ".shared-count")
# 2. Use it in multiple places
Container(
# First component
Text(count_ref, class_name="shared-count"),
# Second component (Function Component)
CountDisplay(count_ref, class_name="shared-count")
)
# 3. Update BOTH with one click!
Button("Increment All", on_click=updateVRef(".shared-count", V(".shared-count").int() + 1))
Dars SSR System & Backend Integration
pip install --upgrade dars-framework
v1.6.9 marks the official release of the Dars SSR System , bringing true full-stack capabilities to the framework. You can now render pages on the server.
The new
dars.backend
module integrates seamlessly with
FastAPI
, allowing you to serve your Dars application alongside your API routes.
create_ssr_app(dars_app)
: Converts your Dars App into a standard ASGI application (FastAPI) capable of server-side rendering.
RouteType.SSR
: A new route type that tells Dars to render the page component on the server before sending it to the client.
# backend/api.py
from fastapi import FastAPI
from dars.backend.ssr import create_ssr_app
from main import app as dars_app
# Create full-stack app
app = create_ssr_app(dars_app)
# Add custom API endpoints
@app.get("/api/custom")
def custom_endpoint():
return {"message": "Hello from API"}
Enable Server-Side Rendering for specific pages with a single parameter:
@route("/dashboard", route_type=RouteType.SSR)
def dashboard():
return Page(
# This is rendered on the server!
Heading("Welcome Back", level=1),
Text(f"Server Time: {datetime.now()}")
)
The CLI has been updated to support the new full-stack architecture.
Create a complete SSR-ready project structure:
dars init my-app --type ssr
This generates:
backend/
directory with
api.py
and
apiConfig.py
main.py
configured for SSR
The system allows you to configure the SSR backend URL dynamically:
# Automatically points to localhost:3000 in dev, and relative in prod
ssr_url = DarsEnv.get_urls()['backend']
app = App(ssr_url=ssr_url)
Component-Level State Updates with updateVRef()
pip install --upgrade dars-framework
Dars Framework v1.6.8 introduces
updateVRef()
, a powerful new function for updating DOM values and component state without requiring State objects. This completes the component-level state management cycle alongside
V()
.
The Complete Cycle:
V("#input")
- Extract values (v1.5.8)
V("#input").length() >= 3
- Boolean validation (v1.6.7)
updateVRef("#input", "new value")
- Update values (v1.6.8)
NEW!
Button(
"Increment",
on_click=dScript("""
const el = document.querySelector('#count');
const current = parseInt(el.textContent);
el.textContent = current + 1;
""")
)
Button(
"Increment",
on_click=updateVRef("#count", V("#count").int() + 1)
)
Benefits:
Update any DOM element value with a single function call:
# Update text content
Button("Set Name", on_click=updateVRef("#name", "John Doe"))
# Update input value
Button("Clear", on_click=updateVRef("#email", ""))
# Update checkbox
Button("Check", on_click=updateVRef("#agree", True))
# Update number
Button("Set Price", on_click=updateVRef("#price", 99.99))
Use V() expressions to create dynamic updates:
# Copy values
Button("Copy", on_click=updateVRef("#target", V("#source")))
# With transformations
Button("Uppercase", on_click=updateVRef("#output", V("#input").upper()))
# With calculations
Button("Calculate", on_click=updateVRef("#total",
V("#price").float() * V("#qty").int()
))
Combine with boolean operators for conditional updates:
# Conditional text
Button("Check Age", on_click=updateVRef("#status",
(V("#age").int() >= 18).then("Adult", "Minor")
))
# Validation messages
Button("Validate", on_click=updateVRef("#message",
(V("#email").includes("@")).then("✓ Valid", "✗ Invalid")
))
Update multiple elements with a single call:
# Clear form
Button("Clear All", on_click=updateVRef({
"#name": "",
"#email": "",
"#age": ""
}))
# Fill sample data
Button("Fill Sample", on_click=updateVRef({
"#name": "John Doe",
"#email": "john@example.com",
"#age": 25
}))
from dars.all import *
@route("/counter")
def counter():
return Page(
Container(
# Display count
Text("0", id="count", style="text-[48px] font-bold"),
# Update buttons
Button("+", on_click=updateVRef("#count", V("#count").int() + 1)),
Button("-", on_click=updateVRef("#count", V("#count").int() - 1)),
Button("Reset", on_click=updateVRef("#count", 0))
)
)
@route("/form")
def form():
return Page(
Container(
Input(id="first-name", placeholder="First Name"),
Input(id="last-name", placeholder="Last Name"),
Input(id="full-name", placeholder="Full Name", readonly=True),
# Auto-generate full name
Button(
"Generate Full Name",
on_click=updateVRef("#full-name",
V("#first-name") + " " + V("#last-name")
)
),
# Clear all
Button(
"Clear All",
on_click=updateVRef({
"#first-name": "",
"#last-name": "",
"#full-name": ""
})
)
)
)
@route("/cart")
def cart():
return Page(
Container(
Input(id="price", input_type="number", value="19.99"),
Input(id="quantity", input_type="number", value="1"),
Text("0", id="total"),
# Calculate total
Button(
"Calculate Total",
on_click=updateVRef("#total",
V("#price").float() * V("#quantity").int()
)
),
# Apply discount
Button(
"Apply 10% Discount",
on_click=updateVRef("#total",
V("#total").float() * 0.9
)
)
)
)
@route("/signup")
def signup():
return Page(
Container(
Input(id="username", placeholder="Username"),
Text("", id="username-status"),
# Validate and normalize
Button(
"Validate Username",
on_click=updateVRef("#username-status",
(V("#username").length() >= 3).then(
"✓ Valid username",
"✗ Too short (need 3+ characters)"
)
)
),
# Auto-fix: lowercase
Button(
"Normalize",
on_click=updateVRef("#username",
V("#username").lower().trim()
)
)
)
)
def updateVRef(
selector: Union[str, Dict[str, Any]],
value: Any = None
) -> dScript
Parameters:
selector
: CSS selector string or dict of {selector: value} pairs
value
: Value to set (string, number, bool, ValueRef, or expression)
Returns:
dScript
object for use in event handlers
Supported Value Types:
"text"
,
42
,
True
V("#source")
V("#input").upper()
V("#a").int() + V("#b").int()
(V("#age").int() >= 18).then("Adult", "Minor")
Supported Elements:
Input
/
Textarea
: Updates
.value
property
Checkbox
/
Radio
: Updates
.checked
property
Select
: Updates
.value
property
.textContent
# Collect, validate, and update
form_data = collect_form(
name=V("#name"),
email=V("#email")
)
# Normalize before submit
Button(
"Normalize & Submit",
on_click=sequence(
updateVRef({
"#name": V("#name").trim(),
"#email": V("#email").lower().trim()
}),
form_data.submit("http://localhost:3000/submit")
)
)
# Smart updates based on validation
Button(
"Update Status",
on_click=updateVRef("#status",
(V("#email").includes("@")).and_(
V("#password").length() >= 8
).then("✓ Ready", "✗ Incomplete")
)
)
# Local updates for UI
Button("Preview", on_click=updateVRef("#preview",
"Name: " + V("#name") + ", Age: " + V("#age")
))
# Save to global state when ready
user = State("user", name="", age=0)
Button("Save", on_click=sequence(
user.name.set(V("#name")),
user.age.set(V("#age").int())
))
updateVRef()
when:
State.set()
when:
FormData
where nested boolean expressions weren't properly serialized
Future enhancements planned for 1.7.0:
useVRef()
- Reactive V() expressions with dependencies
setVRef()
- Set values accessible via V() selectors
Boolean Operators, Form Collection & Backend Integration
pip install --upgrade dars-framework
Dars Framework v1.6.7 introduces a revolutionary declarative system for boolean logic and validation. Write complex conditionals in pure Python using comparison operators - no inline JavaScript required!
Key Features:
==
,
!=
,
>
,
<
,
>=
,
<=
)
.includes()
,
.startswith()
,
.endswith()
,
.length()
,
.bool()
.and_()
and
.or_()
for combining conditions
.then()
for ternary operations
.int()
or
.float()
Button(
"Validate",
on_click=dScript("""
const email = document.querySelector('#email').value;
const age = parseInt(document.querySelector('#age').value);
let valid = false;
if (email.includes('@') && email.includes('.') && age >= 18 && age <= 120) {
valid = true;
}
const message = valid ? '✓ Valid' : '✗ Invalid';
window.Dars.change({
id: 'form',
dynamic: true,
validation: message
});
""")
)
Button(
"Validate",
on_click=form.validation.set(
(V("#email").includes("@")).and_(
V("#email").includes(".")
).and_(
V("#age").int() >= 18
).and_(
V("#age").int() <= 120
).then("✓ Valid", "✗ Invalid")
)
)
Benefits:
# Numeric comparisons (require .int() or .float())
V("#age").int() >= 18
V("#price").float() < 100.0
V("#quantity").int() == 5
# String equality
V("#password") == V("#confirm-password")
V("#email") != ""
# All operators: ==, !=, >, <, >=, <=
# Check if string contains substring
V("#email").includes("@")
# Check string start/end
V("#filename").startswith("report_")
V("#filename").endswith(".pdf")
# Get string length (returns ValueRef with .int())
V("#password").length() >= 8
# Convert to boolean
V("#checkbox").bool()
# AND operator
(V("#age").int() >= 18).and_(V("#age").int() <= 65)
# OR operator
(V("#email").includes("@")).or_(V("#phone").length() >= 10)
# Complex combinations
(V("#name").length() >= 3).and_(
(V("#email").includes("@")).and_(
V("#email").includes(".")
)
)
# Simple conditional
(V("#age").int() >= 18).then("Adult", "Minor")
# With state updates
state.message.set(
(V("#score").int() >= 60).then("Pass", "Fail")
)
# Complex validation
state.validation.set(
(V("#password").length() >= 8).and_(
V("#password") == V("#confirm")
).then("✓ Valid", "✗ Invalid")
)
New in v1.6.7 : Collect and submit form data without writing a single line of JavaScript!
The new
FormData
class and
collect_form()
helper provide a completely declarative way to handle forms using
V()
expressions.
from dars.all import *
# Collect form data with kwargs syntax
form_data = collect_form(
name=V("#name-input"),
email=V("#email-input"),
age=V("#age-input").int(),
is_premium=V("#premium-checkbox")
)
# Show in alert
Button("Submit", on_click=form_data.alert())
# Log to console
Button("Log", on_click=form_data.log())
# Save to state
Button("Save", on_click=form_data.to_state(state.data))
Nested Dictionaries & Lists:
form_data = collect_form(
name=V("#name"),
email=V("#email"),
# Nested validation results
validation={
"email_valid": V("#email").includes("@"),
"age_ok": (V("#age").int() >= 18).and_(
V("#age").int() <= 120)
},
# Conditional values
discount=(V("#premium").bool()).then("10%", "0%"),
# Timestamp
submitted_at=getDateTime()
)
.alert(title)
- Show form data in alert dialog
.log(message)
- Log form data to console
.to_state(property)
- Save form data to state
.submit(url, state_property, on_success, on_error)
- Submit to backend via POST
.submit_and_alert(state_property, title)
- Submit with alert
Submit forms to backend APIs with a single method call:
from dars.all import *
app = App("Form with Backend")
form = State("form", response="")
# Collect form data
form_data = collect_form(
name=V("#name"),
email=V("#email"),
age=V("#age").int(),
submitted_at=getDateTime()
)
@route("/")
def index():
return Page(
Container(
Input(id="name", placeholder="Name"),
Input(id="email", placeholder="Email"),
Input(id="age", input_type="number", placeholder="Age"),
# Submit to backend - NO RAW JAVASCRIPT!
Button(
"Submit to Backend",
on_click=form_data.submit(
url="http://localhost:3000/submit",
state_property=form.response,
on_success=alert("Form submitted successfully!")
)
),
# Display backend response
Container(
Text("Backend Response:", style="font-bold"),
Text(text=useDynamic("form.response"))
)
)
)
app.add_page("index", index())
Features:
Generate client-side timestamps in multiple formats:
# Default ISO format
getDateTime() # "2025-12-04T22:04:09.123Z"
# Different formats
getDateTime("iso") # "2025-12-04T22:04:09.123Z"
getDateTime("locale") # "12/4/2025, 10:04:09 PM"
getDateTime("date") # "12/4/2025"
getDateTime("time") # "10:04:09 PM"
getDateTime("timestamp") # 1733362449123
Usage in Forms:
form_data = collect_form(
name=V("#name"),
email=V("#email"),
submitted_at=getDateTime() # Automatic timestamp
)
Usage with State:
Button("Save", on_click=state.last_updated.set(getDateTime()))
Here's a complete form validation and submission example using all new features:
from dars.all import *
app = App("Complete Form Demo")
form = State("form",
email_valid="",
age_valid="",
password_valid="",
backend_response=""
)
# Collect form data
form_data = collect_form(
name=V("#name"),
email=V("#email"),
age=V("#age").int(),
password=V("#password"),
confirm_password=V("#confirm"),
# Nested validation
validation={
"email_valid": V("#email").includes("@"),
"age_ok": (V("#age").int() >= 18).and_(
V("#age").int() <= 120),
"password_strong": V("#password").length() >= 8,
"passwords_match": V("#password") == V("#confirm")
},
# Timestamp
submitted_at=getDateTime()
)
@route("/")
def index():
return Page(
Container(
# Email validation
Input(id="email", placeholder="Email"),
Button(
"Validate Email",
on_click=form.email_valid.set(
(V("#email").includes("@")).and_(
V("#email").includes(".")
).then("✓ Valid", "✗ Invalid")
)
),
Text(text=useDynamic("form.email_valid")),
# Age validation
Input(id="age", input_type="number", placeholder="Age"),
Button(
"Validate Age",
on_click=form.age_valid.set(
(V("#age").int() >= 18).and_(
V("#age").int() <= 120
).then("✓ Valid", "✗ Must be 18-120")
)
),
Text(text=useDynamic("form.age_valid")),
# Password match
Input(id="password", input_type="password", placeholder="Password"),
Input(id="confirm", input_type="password", placeholder="Confirm"),
Button(
"Check Match",
on_click=form.password_valid.set(
(V("#password") == V("#confirm")).and_(
V("#password").length() >= 8
).then("✓ Match", "✗ No match")
)
),
Text(text=useDynamic("form.password_valid")),
# Submit to backend
Button(
"Submit to Backend",
on_click=form_data.submit(
url="http://localhost:3000/submit",
state_property=form.backend_response,
on_success=alert("Success!")
)
),
# Display response
Text(text=useDynamic("form.backend_response"))
)
)
app.add_page("index", index())
if __name__ == "__main__":
app.rTimeCompile()
Fixed a critical bug where
.int()
and
.float()
transformations were overwriting previous transformations instead of chaining them.
Issue:
# This was failing
V("#name").length() >= 4 # .length() was being overwritten
Fixed:
# Now works correctly
V("#name").length() >= 4 # .length() chains with .int()
Added
to_dscript()
method to
BooleanExpression
,
ConditionalExpression
, and
LogicalExpression
classes to make them compatible with
State.set()
calls.
Future enhancements planned for 1.7.0:
useVRef()
hook for reactive value references
setVRef()
hook for setting values
updateVRef()
helper for value updates
.replace()
,
.split()
,
.join()
)
Custom Utility Styles & State Hot Reload Fix
pip install --upgrade dars-framework
You can now define your own utility classes in
dars.config.json
under the
utility_styles
key. This powerful feature allows you to:
btn-primary
).
border: 1px solid red
) directly within your utility definitions.
Example
dars.config.json
:
{
"utility_styles": {
"btn-primary": ["bg-blue-600", "text-white", "p-3", "rounded-lg"],
"card-fancy": ["bg-white", "shadow-lg", "border: 1px solid #e5e7eb"]
}
}
State
objects were duplicated during hot reloads, leading to stale data and unpredictable behavior. The state registry is now properly cleared on every reload.
border-
utilities to correctly distinguish between Tailwind-like classes and raw CSS
border:
properties.
styling
.
config
with details on
utility_styles
and other configuration options.
Massive Styling Expansion & SPA Fixes
pip install --upgrade dars-framework
Dars v1.6.5 brings a colossal update to the utility styling system, making it nearly feature-complete with modern utility-first CSS frameworks like Tailwind.
New Features:
cyan
,
teal
,
lime
,
amber
,
emerald
,
fuchsia
,
rose
,
zinc
,
neutral
, and
stone
.
fs-[value]
utility for setting exact font sizes (e.g.,
fs-[14px]
).
ffam-[value]
utility for setting font families (e.g.,
ffam-sans
,
ffam-[Open_Sans]
).
scale
,
rotate
,
translate
,
skew
,
blur
,
brightness
,
contrast
, etc.
backdrop-blur
,
backdrop-brightness
, etc.
table
,
contents
,
flow-root
,
object-fit
,
z-index
, and more.
Resolved a critical issue where
hover_style
and
active_style
were not being generated for Single Page Applications (SPA) using the
@route
decorator.
styling.md
.
operations.md
.
CONTRIBUTING.md
for community guidelines.
Pythonic Mathematical Expressions - Declarative Calculations
pip install --upgrade dars-framework
Dars Framework v1.6.4 introduces a revolutionary declarative system for mathematical expressions. Write complex calculations in pure Python using operator overloading - no inline JavaScript required!
Key Features:
+
,
-
,
*
,
/
,
%
,
**
)
.float()
or
.int()
Button(
"Calculate",
on_click=dScript(f"""
const n1 = parseFloat(document.querySelector('.num1').value);
const n2 = parseFloat(document.querySelector('.num2').value);
const op = document.querySelector('.operation').value;
let result = 0;
switch(op) {{
case '+': result = n1 + n2; break;
case '-': result = n1 - n2; break;
case '*': result = n1 * n2; break;
case '/': result = n1 / n2; break;
}}
window.Dars.change({{
id: 'calc',
dynamic: true,
result: result
}});
""")
)
Button(
"Calculate",
on_click=calc.result.set(
V(".num1").float() + V(".operation").operator() + V(".num2").float()
)
)
Benefits:
# Addition
calc.total.set(V(".price").float() + V(".tax").float())
# Subtraction
calc.change.set(V(".paid").float() - V(".total").float())
# Multiplication
calc.total.set(V(".price").float() * V(".quantity").int())
# Division
calc.average.set(V(".sum").float() / V(".count").int())
# a + b * c → a + (b * c) ✅ Automatic
calc.result.set(
V(".a").float() + V(".b").float() * V(".c").float()
)
# (a + b) * c → (a + b) * c ✅ Preserved
calc.result.set(
(V(".a").float() + V(".b").float()) * V(".c").float()
)
# With literals
calc.total.set(
(V(".price").float() * V(".qty").int()) * 1.15 # Add 15% tax
)
Precedence Table:
| Operator | Precedence | Associativity |
|---|---|---|
**
|
3 (highest) | Right |
*
,
/
,
%
|
2 | Left |
+
,
-
|
1 (lowest) | Left |
Use operators from Select or Input elements with the new
.operator()
method:
from dars.all import *
calc = State("calc", operation="+", result=0)
@route("/")
def index():
return Page(
# Operation selector
Select(
value=useValue("calc.operation", selector=".operation"),
class_name="operation",
options=[
SelectOption("+", "➕ Add"),
SelectOption("-", "➖ Subtract"),
SelectOption("*", "✖️ Multiply"),
SelectOption("/", "➗ Divide")
]
),
# Number inputs
Input(class_name="num1", input_type="number"),
Input(class_name="num2", input_type="number"),
# Declarative calculation!
Button(
"Calculate",
on_click=calc.result.set(
V(".num1").float() + V(".operation").operator() + V(".num2").float()
)
),
# Result display
Text(text=useDynamic("calc.result"))
)
How it works:
V(".operation").operator()
extracts the operator value
+
,
-
,
*
,
/
,
%
,
**
+
if invalid (with console warning)
All mathematical expressions include automatic NaN validation:
# Empty inputs automatically return 0
Button(
"Calculate",
on_click=calc.result.set(
V(".num1").float() + V(".num2").float()
)
)
# Empty inputs → Returns 0
# Console: "[Dars] Invalid input: one or more values are NaN. Returning 0."
Validation Points:
V()
for value extraction
Select(
value=useValue("calc.operation", selector=".operation"),
class_name="operation",
options=[...]
)
Future enhancements planned for 1.7.0:
>
,
<
,
==
,
!=
)
and
,
or
,
not
)
.abs()
,
.round()
,
.sqrt()
, etc.)
Tailwind-like Utility Class System
pip install --upgrade dars-framework
Dars Framework now includes a powerful, Python-native utility class system inspired by Tailwind CSS. You can style your components using concise utility strings directly in your Python code, without any external build tools or Node.js dependencies.
Key Features:
Component
props.
Example:
from dars.all import *
def MyComponent():
return Container(
Text("Hello, Dars!"),
style="bg-blue-500 p-4 rounded-lg shadow-md hover:bg-blue-600 transition-all",
hover_style="scale-105",
active_style="scale-95"
)
The system supports a wide range of utilities including:
flex
,
grid
,
block
,
hidden
,
flex-row
,
justify-center
,
items-center
,
gap-4
p-4
,
m-4
,
px-2
,
py-2
(using rem units)
w-full
,
h-screen
,
w-1/2
,
max-w-md
text-xl
,
font-bold
,
text-center
,
text-blue-500
bg-red-500
,
bg-gray-100
border
,
border-2
,
rounded-lg
,
rounded-full
shadow-md
,
opacity-50
,
cursor-pointer
For values not in the standard scale, use square brackets:
style="w-[350px] bg-[#1a2b3c] z-[100] top-[50px]"
Utility strings work seamlessly with Dars State management for dynamic styling:
state = State("theme", style="bg-gray-100 p-4")
Container(
"Content",
style=useDynamic("theme.style"), # Binds directly to the utility string
)
useValue Enhancements & Critical Hot Reload Fixes
pip install --upgrade dars-framework
useValue
Hook - Simplified Usage
The
useValue
hook has been improved to support direct usage in built-in components without requiring a selector. This makes it easier to set initial (non-reactive) values from state.
Simplified Syntax:
# Before (required selector)
Text(text=useValue("user.name", ".my-text"))
# Now (selector optional)
Text(text=useValue("user.name"))
This is perfect for initializing components with state values that don't need to update reactively or be extracted back later.
useDynamic
Numeric Value Display
Fixed an issue where
useDynamic
would display internal marker strings (e.g.,
__DARS_DYNAMIC_...
) instead of the actual value when the state property was a number (especially
0
). Numeric values are now correctly resolved and displayed in both initial render and reactive updates.
Resolved a critical issue where state changes were not reflected after a hot reload. The state registry now correctly deduplicates state objects, ensuring that the most recent version of the state is always used. This fixes cases where changing a default value in code (e.g.,
count=0
to
count=2
) wouldn't update the UI.
Side Effects System Enhancement - useWatch Arrays & Multiple Callbacks
pip install --upgrade dars-framework
The
useWatch
hook now supports watching multiple state properties simultaneously and executing multiple callbacks, enabling powerful reactive patterns and side effects.
Watch Multiple State Properties:
# Watch multiple properties - callback executes when ANY of them change
app.useWatch(
["product.name", "product.price"],
productState.info.set("Product: " + V("product.name") + " - $" + V("product.price"))
)
Multiple Callbacks:
# Execute multiple callbacks when state changes
app.useWatch(
"cart.total",
log("Total changed!"),
alert("Cart updated")
)
# Combine array syntax with multiple callbacks
app.useWatch(
["product.name", "product.price"],
productState.info.set("Product: " + V("product.name") + " - $" + V("product.price")),
log("Product info updated")
)
Key Features:
Resolved an issue where custom state properties (properties other than
text
,
html
,
style
,
attrs
) were not correctly triggering watchers when updated via
change()
.
What was fixed:
info
,
count
,
status
now correctly update in
st.values
Example that now works correctly:
productState = State("product", name="Milk", price=100, info="")
# This now correctly updates product.info when name or price changes
app.useWatch(
["product.name", "product.price"],
productState.info.set("Product: " + V("product.name") + " - $" + V("product.price"))
)
The
V()
helper now correctly retrieves current state values from the state registry instead of reading stale values from the DOM.
Previous Behavior:
V("product.name")
would read the displayed text from the DOM
Fixed Behavior:
V("product.name")
now reads directly from
window.Dars.getState('product').values['name']
Critical Bug Fixes & State Management Improvements
pip install --upgrade dars-framework
Fixed critical bugs where
useDynamic
and
useValue
hooks were not properly displaying default values from
State
objects when used in
FunctionComponent
s.
Issues Resolved:
useDynamic
now correctly retrieves default values from the state registry even when props are not explicitly passed to the component
useValue
now works correctly without requiring a selector parameter
Example:
# This now works correctly!
state = State("ui", count=0, disabled=False)
@FunctionComponent
def Counter(**props):
return f'''
<div>
<p>Count: {useDynamic("ui.count")}</p>
<button disabled="{useDynamic("ui.disabled")}">Click</button>
</div>
'''
Completely rewrote the
reset()
function to correctly handle
all
component properties, including boolean attributes like
checked
,
disabled
,
readonly
, and
required
.
Previous Behavior:
"false"
string instead of being removed
disabled="false"
would still disable elements (incorrect HTML behavior)
Fixed Behavior:
False
checked
,
disabled
,
readonly
,
required
,
selected
,
autofocus
,
autoplay
,
controls
,
loop
,
muted
Example:
state = State("ui", is_disabled=False, is_checked=True)
Button(disabled=useDynamic("ui.is_disabled"))
Checkbox(checked=useDynamic("ui.is_checked"))
# Reset now works perfectly
Button("Reset All", on_click=state.reset())
Fixed
SyntaxError
in generated JavaScript caused by HTML IDs containing hyphens being used as variable names.
Issue:
// Generated invalid JS
const el_control-panel = ... // SyntaxError!
Fixed:
// Now generates valid JS
const el_control_panel = ... // ✓ Valid
Enhanced the reactive binding system to properly handle boolean attributes with the
is_
prefix.
Supported Patterns:
# All of these now work correctly
state = State("ui", is_disabled=False, disabled=False)
# Automatic mapping: is_disabled -> disabled attribute
Button(disabled=useDynamic("ui.is_disabled"))
# State changes properly update the DOM
state.is_disabled.set(True) # Adds disabled attribute
state.is_disabled.set(False) # Removes disabled attribute completely
[!IMPORTANT] When using
Stateobjects with hooks likeuseDynamicanduseValue, the state ID should NOT match any component ID in your DOM. The state ID is a unique identifier for the state object itself, not a component.
X Incorrect:
# DON'T do this - state ID matches button ID
state = State("my-button", count=0)
Button(id="my-button", text=useDynamic("my-button.count"))
✓ Correct:
# DO this - state has unique ID
state = State("counter-state", count=0)
Button(id="my-button", text=useDynamic("counter-state.count"))
The reactive system uses watchers to update components when state changes, so the state ID doesn't need to (and shouldn't) match any specific component ID.
js_lib.py
change()
function
: Added intelligent boolean attribute detection with
is_
prefix support
_applyMods()
function
: Enhanced to handle boolean attributes in state modifications
_restoreDefault()
function
: Improved to correctly restore boolean attributes to their initial state
html_css_js.py
_generate_reactive_bindings_js()
: Added boolean attribute handling in watcher code generation
_process_function_component()
: Added fallback to
STATE_V2_REGISTRY
for missing prop values
use_value.py
ValueMarker.__init__()
: Now always registers markers, even without a selector
Unified Keyboard Events & KeyCode System
pip install --upgrade dars-framework
on_key_press
)
Simplified keyboard event handling by consolidating
on_key_down
and
on_key_up
into a single, robust
on_key_press
event. This ensures consistent behavior across all components and browsers.
Deprecated:
on_key_down
on_key_up
New Standard:
Input(on_key_press=log("Key pressed!"))
KeyCode
System
No more magic strings or numbers! The new
KeyCode
class provides constants for all keyboard keys, making your code readable and maintainable.
from dars.all import KeyCode
# Use constants
KeyCode.ENTER
KeyCode.ESCAPE
KeyCode.TAB
KeyCode.SPACE
KeyCode.A
KeyCode.F1
onKey
,
switch
,
addGlobalKeys
Introduced three powerful helpers to make keyboard handling elegant and Pythonic.
onKey()
- Single Key Handler
Handle specific keys with optional modifiers easily:
# Simple
Input(on_key_press=onKey(KeyCode.ENTER, submit_form()))
# With Modifiers
Container(on_key_press=onKey(KeyCode.S, save(), ctrl=True))
switch()
- Multiple Key Handler
Handle multiple keys in a single component without messy if-statements:
Input(on_key_press=switch({
KeyCode.ENTER: submit_form(),
KeyCode.ESCAPE: clear_form(),
KeyCode.TAB: focus_next()
}))
addGlobalKeys()
- App-wide Shortcuts
Register global keyboard shortcuts that work anywhere in your app:
addGlobalKeys(app, {
(KeyCode.S, 'ctrl'): save_document(),
(KeyCode.Z, 'ctrl'): undo(),
(KeyCode.Z, 'ctrl', 'shift'): redo()
})
A comprehensive guide to the new system is available in
KeyEvents.md
, covering everything from basic usage to advanced global shortcuts and best practices.
From
on_key_down
/
on_key_up
:
Simply rename your event handlers to
on_key_press
. The underlying behavior uses
keydown
for maximum reliability.
# Before
Input(on_key_down=dScript("..."))
# After
Input(on_key_press=dScript("..."))
Enhanced V() Helper & useValue Selector Support
pip install --upgrade dars-framework
The
V()
helper now supports extracting values from
reactive state
in addition to DOM elements!
# Extract from reactive state created by useDynamic()
V("cart.total") # Gets current value of cart.total
V("user.name") # Gets current value of user.name
V("product.price") # Gets current value of product.price
How it works:
V("cart.total")
finds the reactive element created by
useDynamic("cart.total")
textContent
value
from dars.all import *
productState = State("product", price=19.99, quantity=1, total=19.99)
@FunctionComponent
def ProductCard(**props):
return f'''
<div {Props.id}>
<p>Price: ${useDynamic("product.price")}</p>
<input type="number" value="{useValue("product.quantity", ".qty-input")}" />
<p>Total: ${useDynamic("product.total")}</p>
</div>
'''
# Calculate total: DOM input × State value
Button("Calculate", on_click=productState.total.set(
V(".qty-input").int() * V("product.price").float()
))
Breaking Change (Validation) : Arithmetic operators now require numeric transformations to prevent bugs.
Previously, you could accidentally multiply strings:
# Before: This would concatenate strings, not multiply!
V("#price") * V("#qty") # "19.99" * "5" = NaN or unexpected behavior
Arithmetic operators (
*
,
/
,
-
,
%
,
**
) now require
.int()
or
.float()
:
# CORRECT - With transformations
V("#price").float() * V("#qty").int() # 19.99 * 5 = 99.95
# ERROR - Without transformations
V("#price") * V("#qty")
# TypeError: Multiplication requires numeric transformation.
# Use V('#price').int() or V('#price').float() before multiplying.
String concatenation (
+
) still works without transformations:
# Always allowed
V("#first") + " " + V("#last") # String concatenation
"Total: $" + V("cart.total") # String concatenation
Supported Operators:
+
- Addition/Concatenation (always allowed)
*
- Multiplication (requires
.int()
or
.float()
)
/
- Division (requires
.int()
or
.float()
)
-
- Subtraction (requires
.int()
or
.float()
)
%
- Modulo (requires
.int()
or
.float()
)
**
- Power (requires
.int()
or
.float()
)
useValue()
now supports automatic selector application in FunctionComponents!
@FunctionComponent
def UserForm(**props):
return f'''
<div {Props.id}>
<input value="{useValue("user.name", ".name-input")}" />
<input value="{useValue("user.email", "#email-field")}" />
</div>
'''
# Extract values using the selectors
Button("Save", on_click=userState.name.set(V(".name-input")))
How it works:
useValue("user.name", ".name-input")
sets initial value AND applies class
name-input
V(".name-input")
extracts the current value (even if modified by user)
Supported selectors:
.foo
) → Added to element's
class
attribute
#bar
) → Set as element's
id
attribute
If you were using arithmetic operators without transformations, add
.int()
or
.float()
:
Before:
state.total.set(V("#price") * V("#qty"))
After:
state.total.set(V("#price").float() * V("#qty").int())
String concatenation is unchanged:
# Still works the same
state.fullname.set(V("#first") + " " + V("#last"))
from dars.all import *
app = App("Shopping Cart")
# Reactive state
cartState = State("cart", total=0.0)
productState = State("product", name="Widget", price=19.99, quantity=1)
@FunctionComponent
def ProductCard(**props):
return f'''
<div {Props.id} {Props.class_name} {Props.style}>
<!-- Reactive display -->
<h3>{useDynamic("product.name")}</h3>
<p>Price: ${useDynamic("product.price")}</p>
<!-- Editable quantity with selector -->
<input type="number"
value="{useValue("product.quantity", ".qty-input")}"
min="1" />
<!-- Reactive total -->
<p>Total: ${useDynamic("cart.total")}</p>
</div>
'''
@route("/")
def index():
return Page(
ProductCard(id="product-card", name="Milk", price=100, quantity=2, total=0),
# Calculate: DOM input × State value
Button("Calculate Total", on_click=cartState.total.set(
V(".qty-input").int() * V("product.price").float()
)),
# String concatenation (no transformation needed)
Button("Show Info", on_click=productState.name.set(
"Product: " + V("product.name") + " - $" + V("product.price")
)
)
)
app.add_page("index", index(), title="Product", index=True)
# Watch for changes
app.useWatch("cart.total", log("Cart total changed!"))
if __name__ == "__main__":
app.rTimeCompile()
useValue()
were not being applied to FunctionComponent elements
useValue()
now works identically in FunctionComponents and built-in components
"cart.total"
vs
".cart-total"
)
useDynamic()
,
useValue()
, and
V()
Change
: Arithmetic operators (
*
,
/
,
-
,
%
,
**
) now require
.int()
or
.float()
transformations.
Reason : Prevents accidental string operations that cause bugs.
Migration
: Add
.int()
or
.float()
before arithmetic operations:
V("#price").float() * V("#qty").int()
Note
: String concatenation (
+
) is unchanged and still works without transformations.
useValue()
,
useDynamic()
, and
V()
The enhanced V() helper and complete hooks integration pave the way for more advanced reactive patterns and seamless state management in future releases.
useValue hook & Value Access Helpers
pip install --upgrade dars-framework
useValue
Hook
The new
useValue
hook allows you to access the
initial value
of a state property without creating a reactive binding. This is perfect for form inputs where you want to set a default value but allow the user to edit it freely.
# Initial value from state, but editable by user
Input(value=useValue("user.name"))
# In FunctionComponent templates (resolves to initial value string)
@FunctionComponent
def Profile(**props):
return f"<div>{useValue('user.name')}</div>"
V
,
url
,
transform
)
Introducing a set of helpers to make working with DOM values completely Pythonic, eliminating the need for
RawJS
.
Select DOM elements and perform operations directly in Python:
# Concatenation
Button("Combine", on_click=state.fullname.set(
V("#first") + " " + V("#last")
))
# Transformations
Button("Upper", on_click=state.name.set(V("#name").upper()))
Button("Add", on_click=state.count.set(V("#num1").int() + 10))
Construct dynamic URLs easily with proper interpolation:
Button("Fetch", on_click=fetch(
url("https://api.example.com/users/{id}", id=V("#userId"))
))
Enhanced
useDynamicSupport & StateV2 Fixes
pip install --upgrade dars-framework
useDynamic
Support for Built-in Components
useDynamic
now works with
all properties
of built-in components, including:
# Dynamic image source and alt text
Image(
src=useDynamic("product.imageUrl"),
alt=useDynamic("product.name")
)
# Dynamic link href and text
Link(
href=useDynamic("navigation.url"),
text=useDynamic("navigation.label")
)
All boolean attributes now support dynamic binding:
# Dynamic disabled state
Button(
text="Submit",
disabled=useDynamic("form.isSubmitting")
)
# Dynamic checked state
Checkbox(
checked=useDynamic("settings.notifications"),
label="Enable Notifications"
)
# Dynamic readonly and required
Input(
value=useDynamic("user.email"),
readonly=useDynamic("form.isLocked"),
required=useDynamic("form.emailRequired")
)
Supported Boolean Properties:
disabled
- Button, Input, Textarea, Checkbox, RadioButton, Select, Slider
checked
- Checkbox, RadioButton
readonly
- Input, Textarea
required
- Input, Textarea, Checkbox, RadioButton, Select
increment()
&
decrement()
Fixes
Fixed critical issues with
StateV2
reactive operations:
Previously,
increment()
and
decrement()
only worked on properties named
text
. Now they work on
any numeric property
:
# Before: This would error
state = State("counter", count=0)
Button("Increment", on_click=state.count.increment(by=1)) # ❌ ValueError
# After: Works perfectly!
state = State("counter", count=0)
Button("Increment", on_click=state.count.increment(by=1)) # ✅ Works!
Increment/decrement operations now correctly persist across multiple clicks:
state = State("counter", count=0)
# Before: Would increment 0→1, then stay at 1 forever
# After: Correctly increments 0→1→2→3→...
Button("Increment", on_click=state.count.increment(by=1))
Button("Decrement", on_click=state.count.decrement(by=1))
Technical Details:
window.Dars.getState()
increment()
/
decrement()
use
window.Dars.change()
for proper state updates
useDynamic
for each component type
Added documentation for visualization components in
components.md
:
Chart Component:
import plotly.graph_objects as go
fig = go.Figure(data=[go.Bar(x=['A', 'B', 'C'], y=[1, 3, 2])])
Chart(figure=fig, width="100%", height="400px")
DataTable Component:
import pandas as pd
df = pd.DataFrame({
'Name': ['Alice', 'Bob'],
'Age': [25, 30]
})
DataTable(data=df, theme="striped", page_size=10)
increment()
raised
ValueError
for non-
text
properties
int
or
float
) instead of checking property name
increment()
and
decrement()
None.
The enhanced
useDynamic
system and robust StateV2 operations pave the way for more advanced reactive patterns and state management features in future releases.
Hooks System Enhanced :
useDynamicfor built-in components and newuseWatchhook.
pip install --upgrade dars-framework
useDynamic
for Built-in Components
You can now use
useDynamic
directly in properties of built-in components!
# Bind directly to component props
Text(text=useDynamic("user.name"))
Input(value=useDynamic("user.name"))
Button(text=useDynamic("user.status"))
This works for
Text
,
Button
,
Input
,
Textarea
, and more.
useWatch
Hook
Monitor state changes and execute side effects with
useWatch
.
# Global watcher
app.useWatch("cart.total", log("Cart updated!"))
# Page-specific watcher
@route("/cart")
def cart_page():
page = Page()
page.useWatch("cart.total", log("Total changed!"))
return page
New methods
app.useWatch()
and
page.useWatch()
make integration seamless.
Hooks System & Reactive Bindings relaunch with correct license.
pip install --upgrade dars-framework
useDynamic()
Dars Framework introduces its
first hook
-
useDynamic()
- enabling reactive state bindings in FunctionComponents!
Create components that automatically update when state changes:
from dars.all import *
# Create state
userState = State("user", name="John Doe", email="john@example.com")
# Use useDynamic in FunctionComponent
@FunctionComponent
def UserCard(**props):
return f'''
<div {Props.id} {Props.class_name} {Props.style}>
<h3>{useDynamic("user.name")}</h3>
<p>{useDynamic("user.email")}</p>
</div>
'''
# Render
card = UserCard(id="userCard")
# Update - DOM automatically reacts!
app.add_script(userState.name.set("Jane Doe")) # UI updates instantly
useDynamic()
inserts reactive spans with initial prop values
.set()
, the hook intercepts the change
getInputValue()
Utility
New utility function specifically designed for State V2 integration:
state = State("product", title="", price=0)
Button("Save", on_click=[
state.title.set(getInputValue("titleInput")),
state.price.set(getInputValue("priceInput"))
])
Features:
State.set()
and other dynamic operations
parent_id
parameter for scoped searches
getValue()
for assignments
Difference from
getValue()
:
getValue(input_id, target_id)
- Performs assignment (sets textContent)
getInputValue(input_id)
- Returns value expression (for State.set())
accent-color
support for form controls
Components now respect theme colors:
--dars-primary: #007bff;
--dars-spacing-md: 12px;
/* ... and more */
# Inline styles work better now
Button("Click", style={"width": "200px", "padding": "16px"})
# Global styles via app.add_global_styles()
app.add_global_styles("""
.dars-button {
border-radius: 8px;
}
""")
# Now works correctly across all pages
@route("/docs")
def docs():
return Page(Markdown(content=code_example))
@route("/tutorial")
def tutorial():
return Page(Markdown(content=tutorial_code)) # Also highlighted now!
None! This release is 100% backwards compatible.
User Profile with Reactive Updates:
from dars.all import *
app = App("Reactive Profile")
# State for user data
userState = State("user",
name="John Doe",
email="john@example.com"
)
# Reactive display component
@FunctionComponent
def ProfileDisplay(**props):
return f'''
<div {Props.id} {Props.class_name} {Props.style}>
<h2>{useDynamic("user.name")}</h2>
<p>Email: {useDynamic("user.email")}</p>
</div>
'''
# Edit form
def ProfileEditor():
return Container(
Input(id="nameInput", value="John Doe"),
Input(id="emailInput", value="john@example.com"),
Button("Save", on_click=[
userState.name.set(getInputValue("nameInput")),
userState.email.set(getInputValue("emailInput"))
])
)
# Page
@route("/")
def index():
return Page(
ProfileDisplay(id="profile"),
ProfileEditor()
)
app.add_page("index", index())
if __name__ == "__main__":
app.rTimeCompile()
Result : Edit the inputs and click Save - the profile display updates instantly!
No migration needed! Add
useDynamic()
to new or existing FunctionComponents:
Before (static):
@FunctionComponent
def UserCard(name, email, **props):
return f'<div {Props.id}><h3>{name}</h3><p>{email}</p></div>'
After (reactive):
@FunctionComponent
def UserCard(**props):
return f'''
<div {Props.id}>
<h3>{useDynamic("user.name")}</h3>
<p>{useDynamic("user.email")}</p>
</div>
'''
New documentation added:
useDynamic()
and future hooks
getInputValue()
- Usage guide in Scripts documentation
Visit the Dars Documentation for more details.
The hooks system opens the door for more reactive features...
Hooks System & Reactive Bindings
pip install --upgrade dars-framework
useDynamic()
Dars Framework introduces its
first hook
-
useDynamic()
- enabling reactive state bindings in FunctionComponents!
Create components that automatically update when state changes:
from dars.all import *
# Create state
userState = State("user", name="John Doe", email="john@example.com")
# Use useDynamic in FunctionComponent
@FunctionComponent
def UserCard(**props):
return f'''
<div {Props.id} {Props.class_name} {Props.style}>
<h3>{useDynamic("user.name")}</h3>
<p>{useDynamic("user.email")}</p>
</div>
'''
# Render
card = UserCard(id="userCard")
# Update - DOM automatically reacts!
app.add_script(userState.name.set("Jane Doe")) # UI updates instantly
useDynamic()
inserts reactive spans with initial prop values
.set()
, the hook intercepts the change
getInputValue()
Utility
New utility function specifically designed for State V2 integration:
state = State("product", title="", price=0)
Button("Save", on_click=[
state.title.set(getInputValue("titleInput")),
state.price.set(getInputValue("priceInput"))
])
Features:
State.set()
and other dynamic operations
parent_id
parameter for scoped searches
getValue()
for assignments
Difference from
getValue()
:
getValue(input_id, target_id)
- Performs assignment (sets textContent)
getInputValue(input_id)
- Returns value expression (for State.set())
accent-color
support for form controls
Components now respect theme colors:
--dars-primary: #007bff;
--dars-spacing-md: 12px;
/* ... and more */
# Inline styles work better now
Button("Click", style={"width": "200px", "padding": "16px"})
# Global styles via app.add_global_styles()
app.add_global_styles("""
.dars-button {
border-radius: 8px;
}
""")
# Now works correctly across all pages
@route("/docs")
def docs():
return Page(Markdown(content=code_example))
@route("/tutorial")
def tutorial():
return Page(Markdown(content=tutorial_code)) # Also highlighted now!
None! This release is 100% backwards compatible.
User Profile with Reactive Updates:
from dars.all import *
app = App("Reactive Profile")
# State for user data
userState = State("user",
name="John Doe",
email="john@example.com"
)
# Reactive display component
@FunctionComponent
def ProfileDisplay(**props):
return f'''
<div {Props.id} {Props.class_name} {Props.style}>
<h2>{useDynamic("user.name")}</h2>
<p>Email: {useDynamic("user.email")}</p>
</div>
'''
# Edit form
def ProfileEditor():
return Container(
Input(id="nameInput", value="John Doe"),
Input(id="emailInput", value="john@example.com"),
Button("Save", on_click=[
userState.name.set(getInputValue("nameInput")),
userState.email.set(getInputValue("emailInput"))
])
)
# Page
@route("/")
def index():
return Page(
ProfileDisplay(id="profile"),
ProfileEditor()
)
app.add_page("index", index())
if __name__ == "__main__":
app.rTimeCompile()
Result : Edit the inputs and click Save - the profile display updates instantly!
No migration needed! Add
useDynamic()
to new or existing FunctionComponents:
Before (static):
@FunctionComponent
def UserCard(name, email, **props):
return f'<div {Props.id}><h3>{name}</h3><p>{email}</p></div>'
After (reactive):
@FunctionComponent
def UserCard(**props):
return f'''
<div {Props.id}>
<h3>{useDynamic("user.name")}</h3>
<p>{useDynamic("user.email")}</p>
</div>
'''
New documentation added:
useDynamic()
and future hooks
getInputValue()
- Usage guide in Scripts documentation
Visit the Dars Documentation for more details.
The hooks system opens the door for more reactive features...
Function Components
pip install --upgrade dars-framework
Dars Framework now features Function Components as the primary and recommended way to create custom UI components. This new system provides a clean, Pythonic approach to component creation without the complexity of class inheritance.
Function Components allow you to create reusable UI elements using simple Python functions with f-string templates. The framework automatically handles IDs, styling, events, and other properties.
Key Benefits:
id
,
class_name
,
style
, and
children
automatically
from dars.all import FunctionComponent, Props
@FunctionComponent
def UserCard(name, email, **props):
return f"""
<div {Props.id} {Props.class_name} {Props.style}>
<h3>{name}</h3>
<p>{email}</p>
<div class="card-body">
{Props.children}
</div>
</div>
"""
# Usage
card = UserCard("John Doe", "john@example.com", id="user-1", style={"padding": "20px"})
Option 1: Using Props Helper (Recommended)
@FunctionComponent
def MyComponent(**props):
return f"""
<div {Props.id} {Props.class_name} {Props.style}>
{Props.children}
</div>
"""
Option 2: Explicit Arguments
@FunctionComponent
def MyComponent(id, class_name, style, children, **props):
return f"""
<div {id} {class_name} {style}>
{children}
</div>
"""
Both patterns are fully supported and avoid linter warnings about undefined variables.
The new
Props
class provides static constants for framework properties:
Props.id # "{id}"
Props.class_name # "{class_name}"
Props.style # "{style}"
Props.children # "{children}"
These resolve to the correct placeholders that the framework replaces during rendering.
Important Concept: State V2 updates DOM properties, not arbitrary component arguments.
Correct Usage:
@FunctionComponent
def Counter(**props):
return f"""
<div {Props.id}>
Current count: {Props.children}
</div>
"""
counter = Counter(id="my-counter", children="0")
state = State(counter, text="Current count: 0")
# Updates textContent of the div
Button("Increment", on_click=state.text.set("Current count: 5"))
Why:
Dars exports to static HTML/JS. The JavaScript runtime can only manipulate DOM properties (
textContent
,
style
,
innerHTML
), not re-execute Python functions with new arguments.
Backend HTTP Utilities & Pythonic API Communication
pip install --upgrade dars-framework
Dars now includes a comprehensive
Pythonic system
for HTTP requests and API communication without writing any JavaScript. The new
dars.backend
module enables you to fetch data, bind it to components, and create reactive UIs entirely in Python.
Key Features:
useData()
with dot notation for nested data access
.then()
chaining for sequential operations
from dars.all import *
from dars.backend import get, post, useData
# GET request with data binding
fetch_btn = Button(
"Fetch User",
on_click=get(
id="userData",
url="https://api.example.com/users/1",
callback=name_state.text.set(useData('userData').name)
)
)
# POST request
submit_btn = Button(
"Submit",
on_click=post(
id="result",
url="https://api.example.com/submit",
body={"name": "John", "email": "john@example.com"},
callback=status_state.text.set("✅ Submitted!")
)
)
Access fetched data using Pythonic dot notation:
# Access nested properties
useData('userData').name # → window.userData?.name
useData('user').address.city # → window.user?.address?.city
useData('posts').items[0].title # → window.posts?.items?.[0]?.title
Chain multiple state updates sequentially:
callback=(
status_state.text.set("Loading...")
.then(name_state.text.set(useData('userData').name))
.then(email_state.text.set(useData('userData').email))
.then(status_state.text.set("✅ Loaded!"))
)
Create, update, and delete components dynamically at runtime:
from dars.backend import createComp, updateComp, deleteComp
# Create new component
create_btn.on_click = createComp(
target=Text("Hello!", id="new-item"),
root="container-id",
position="append"
)
# Update component
update_btn.on_click = updateComp(
"my-component-id",
text="Updated!",
style={"color": "red"}
)
# Delete component
delete_btn.on_click = deleteComp("component-id")
Helper functions for working with JSON data:
from dars.backend import stringify, parse, get_value
# Stringify with pretty printing
display_state.text.set(stringify(useData('userData'), pretty=True))
# Parse JSON string
data = parse('{"name": "John"}')
# Safe nested access
city = get_value(useData('userData'), 'address.city', default='Unknown')
State()
now accepts both component objects and string IDs, enabling state management for dynamically created components:
from dars.all import *
from dars.backend import createComp
# Traditional: State with component object
existing_text = Text("0", id="counter")
existing_state = State(existing_text, text=0)
# New: State with string ID (for components created later)
dynamic_state = State("dynamic-counter", text=0)
# Create the component later
create_btn.on_click = createComp(
target=Text("0", id="dynamic-counter"),
root="container-id"
)
# State works even though component was created after state!
increment_btn.on_click = dynamic_state.text.increment(by=1)
Use Cases:
createComp()
New
DataAccessor
class with:
__getattr__
for dot notation support
.code
property for RawJS generation
.bind()
method for StateV2 integration
.get()
method for safe property access
Updated
StateV2._generate_change_call()
to handle
DataAccessor
objects:
DataAccessor
instances
.code
property for JavaScript generation
backend_api.md
- Comprehensive guide (500+ lines) covering:
Documentation URL: https://ztamdev.github.io/Dars-Framework/docs.html#backend-http-utilities
None - This is a fully backward-compatible release. All existing code continues to work.
StateV2
now correctly handles
DataAccessor
objects in
to_js_value()
RawJS
objects are properly serialized in state updates
dScript.then()
chaining works correctly with backend operations
No migration needed - this is an additive release. To start using the new features:
# Add to your imports
from dars.backend import get, post, useData
# Start using Pythonic HTTP
button.on_click = get(
id="data",
url="https://api.example.com/data",
callback=state.text.set(useData('data').message)
)
This release makes Dars fully self-contained for building reactive, API-driven UIs without writing any JavaScript!
Animation System Stability & Runtime Improvements
pip install --upgrade dars-framework
Significant improvements to the animation system's reliability and ease of use:
pulse(iterations="infinite")
now works correctly (fixed
TypeError
).
increment()
can now be chained with animations using
.then()
or
sequence()
.
The client-side runtime has been upgraded to fully support dynamic event handler updates:
# This now works perfectly in real-time
button.on_click = state.update(
text="Clicked!",
on_click=alert("New handler attached!")
)
pulse
animation now correctly handles
iterations="infinite"
(mapped to
Infinity
).
SyntaxError
when chaining state updates (wrapped in async IIFE).
Event Handler Support & Dual State System Documentation
pip install --upgrade dars-framework
State V2 now supports updating event handlers:
from dars.all import *
# Update events in State V2
state.update(
text="Click me",
on_click=alert("Success!"),
on_mouseover=dScript("showTooltip()")
)
# Works with dScript objects
button_state.update(on_click=dScript("console.log('clicked')"))
Implementation:
on_*
properties
.code
from dScript objects
Documentation now presents two coexisting state management systems :
State V2 (Dynamic)
dState/cState (Indexed)
Mod.call()
Comprehensive comparison table and use case guidance added to state management documentation.
_generate_change_call()
properly handles non-JSON-serializable values
state_management.md
to include both systems
Critical Fix : State V2 now properly supports all component properties including
class_name,style,attrs, and more.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.8
Previously (v1.4.7):
State V2 only updated
text
content, incorrectly setting textContent for all properties.
# This didn't work correctly in v1.4.7
counter.class_name.set("active")
Now (v1.4.8):
State V2 properly handles
all component properties
using the
change()
client function.
# All properties now work correctly!
state.text.set("New text")
state.class_name.set("active")
state.style.set({"color": "red"})
state.attrs.set({"title": "Tooltip"})
state.html.set("<strong>Bold</strong>")
All State V2 methods now use the same
change()
function as
this()
and
dState
, ensuring consistent behavior across the framework:
ReactiveProperty.set()
- Works with any property type
State.update()
- Updates multiple properties correctly
State.reset()
- Resets all property types to defaults
Increment/Decrement Validation:
Now properly validates that
increment()
and
decrement()
are only used on numeric properties:
# Correct usage
counter.text.increment(by=1)
# Now throws helpful error
counter.class_name.increment(by=1)
class_name.set()
now correctly updates element's
className
style.set()
now correctly updates inline styles
attrs.set()
now correctly updates HTML attributes
html.set()
now correctly updates
innerHTML
update()
now correctly handles multiple properties simultaneously
reset()
now correctly restores all property types
Root Cause:
State V2 methods were directly manipulating
el.textContent
instead of using the framework's
change()
function, which properly routes updates based on property type.
Solution:
Created
_generate_change_call()
helper that generates proper
change()
payloads:
text
→
{text: value}
html
→
{html: value}
style
→
{style: object}
class_name
→
{attrs: {class: value}}
or
{classes: object}
attrs
→
{attrs: object}
All reactive methods now use this helper for consistent, correct property updates.
from dars.all import *
display = Container(Text("Example"), id="demo")
# Create state with multiple properties
state = State(display,
text="Hello",
class_name="",
style={"background": "#333"}
)
# Update text
text_btn.on_click = state.text.set("Updated!")
# Update CSS class
class_btn.on_click = state.class_name.set("active highlight")
# Update styles
style_btn.on_click = state.style.set({
"background": "linear-gradient(135deg, #667eea, #764ba2)",
"color": "white",
"padding": "20px"
})
# Update HTML attributes
attr_btn.on_click = state.attrs.set({
"data-status": "complete",
"title": "Completed task"
})
# Add/remove specific classes
state.classes.set({
"add": ["active", "highlight"],
"remove": ["disabled", "hidden"]
})
# Toggle classes
state.classes.set({
"toggle": ["expanded"]
})
# Update several properties at once
success_btn.on_click = state.update(
text="Success!",
class_name="success",
style={"color": "green", "fontSize": "18px"},
attrs={"data-result": "ok"}
)
No breaking changes
- all existing code continues to work. However, if you tried using non-
text
properties in v1.4.7 and they didn't work, they will now work correctly in v1.4.8.
If you worked around the limitation:
# Old workaround (v1.4.7)
from dars.core.state import this
button.on_click = this().state(class_name="active") # Had to use this()
# Now works directly with State V2 (v1.4.8)
button.on_click = state.class_name.set("active") # ✅ Works!
rjsmin
dependency (Apache license, replaced with regex fallback)
fastapi
dependency (not used in framework)
JS/CSS minification now uses:
change()
infrastructure
this()
, and dState now all use same property system
Upgrade highly recommended
for all projects using State V2, especially if you need to update component properties beyond just
text
.
Introducing State V2 and comprehensive animation system. This is the biggest update to Dars state management, bringing a pure Pythonic API and 15+ built-in animations.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.7
New
State
Class:
Before (dState - deprecated):
from dars.core.state import dState, Mod
display = Text("0", id="counter")
st = dState("counter", component=display, states=[0, 1, 2])
st.cState(1, mods=[Mod.inc(display, prop='text', by=1)])
button.on_click = st.state(1)
After (State V2 - recommended):
from dars.all import State
display = Text("0", id="counter")
counter = State(display, text=0)
button.on_click = counter.text.increment(by=1)
Key Features:
state.property.operation()
auto_increment()
,
auto_decrement()
state.reset()
to restore initial values
Usage:
from dars.all import *
# Create component and state
timer_display = Text("0", id="timer", style={"font-size": "36px"})
timer = State(timer_display, text=0)
# Auto-incrementing timer
start_btn.on_click = timer.text.auto_increment(by=1, interval=1000)
stop_btn.on_click = timer.text.stop_auto()
reset_btn.on_click = timer.reset()
15+ Built-in Animations:
fadeIn
/
fadeOut
- Opacity transitions
slideIn
/
slideOut
- Position-based slides (8 directions)
scaleIn
/
scaleOut
- Size transformations
shake
- Shake effect for alerts
bounce
- Bounce effect
pulse
- Heartbeat/pulse effect
rotate
- Rotation animations
flip
- Flip on X/Y axis
colorChange
- Color transitions
morphSize
- Size morphing
sequence
- Chain multiple animations
Animation Chaining:
from dars.all import *
button.on_click = sequence(
fadeIn(id="box", duration=400),
pulse(id="box", scale=1.2, iterations=2),
shake(id="box", intensity=5)
)
Integration with State V2:
button.on_click = sequence(
counter.text.increment(by=1),
pulse(id="counter", scale=1.2),
fadeOut(id="counter", duration=200),
counter.text.set(value=0),
fadeIn(id="counter", duration=200)
)
All Animations:
dScript
objects for chaining
New Example Template:
dars/templates/examples/advanced/StateV2/
Template Structure:
dars/templates/examples/advanced/StateV2/
├── index.py # Main application
├── hero_component.py # Hero section
├── counter_component.py # Counter demo
├── timer_component.py # Timer demo
├── animation_component.py # Animation showcase
├── styles.css # Global styles
└── README.md # Documentation
dState/cState Deprecation:
dState
and
cState
are now deprecated
Updated Files:
state_management.md
- Completely rewritten for State V2
scripts.md
- Added comprehensive animation system documentation
Animation System:
transition='none'
pattern
sequence()
function trailing semicolon issue
setTimeout(20ms)
for reliable state application
State Operations:
auto_increment
and
auto_decrement
now return proper
dScript
objects
startLoop
and
stopLoop
setTimeout
void el.offsetWidth
transition='none'
before initial styles
animation.finished
for Web Animations API
sequence()
1. Import Changes:
# Old
from dars.core.state import dState, Mod
# New
from dars.all import State
2. State Creation:
# Old
counter_state = dState("counter", component=display, states=[0, 1, 2])
counter_state.cState(1, mods=[Mod.inc(display, prop='text', by=1)])
# New
counter = State(display, text=0)
3. Event Handlers:
#Old
button.on_click = counter_state.state(1)
# New
button.on_click = counter.text.increment(by=1)
4.Auto Operations (New Feature):
# Only available in State V2
start_btn.on_click = timer.text.auto_increment(by=1, interval=1000)
stop_btn.on_click = timer.text.stop_auto()
from dars.all import fadeIn, pulse, sequence
# Simple animation
button.on_click = fadeIn(id="element", duration=500)
# Chained animations
button.on_click = sequence(
fadeIn(id="box"),
pulse(id="box", scale=1.1)
)
from dars.all import *
app = App("Counter Demo")
# Create display with state
counter_display = Text("0", id="counter", style={
"font-size": "48px",
"color": "#2563eb"
})
counter = State(counter_display, text=0)
# Buttons with operations
inc_btn = Button("+1", on_click=counter.text.increment(by=1))
dec_btn = Button("-1", on_click=counter.text.decrement(by=1))
reset_btn = Button("Reset", on_click=counter.reset())
pulse_btn = Button("Pulse", on_click=pulse(id="counter", scale=1.2))
page = Page(Container(counter_display, inc_btn, dec_btn, reset_btn, pulse_btn))
app.add_page("index", page, index=True)
app.rTimeCompile()
from dars.all import *
app = App("Timer Demo")
timer_display = Text("0", id="timer")
timer = State(timer_display, text=0)
start_btn = Button("Start", on_click=timer.text.auto_increment(by=1, interval=1000))
stop_btn = Button("Stop", on_click=timer.text.stop_auto())
reset_btn = Button("Reset", on_click=timer.reset())
page = Page(Container(timer_display, start_btn, stop_btn, reset_btn))
app.add_page("index", page, index=True)
app.rTimeCompile()
Still in BETA - State V2 and animations fully supported in both web and desktop exports.
This is a landmark release - State V2 represents the future of Dars state management. Upgrade highly recommended for all projects.
Major Feature Release : Introduces Single Page Application (SPA) support and a powerful client-side routing system.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.5
New Routing System:
parent
parameter and the new
Outlet
component.
Robust Error Handling:
app.set_404_page()
.
Hot Reload Stability:
Critical bug fix release. Removes non-functional features, fixes style merge, and improves state restoration.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.4
Mod.set()
now correctly merges style properties
this().goto()
- Non-functional feature removed; use
state.state()
instead
js_lib.py
State management enhancements with compile-time validation, component self-navigation, and critical style merge fix. Improves developer experience and fixes property replacement bug.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.3
this().goto(idx)
- Component self-navigation for state transitions
this_for(id)
- Compile-time validation helper for state navigation
Mod.set()
now merges style properties instead of replacing them
New utility dScript functions added to
utils_dsfor enhanced client-side interactions.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.2
utils_ds
)
setTimeout(delay: int, code: dScript)
: Execute a dScript after a delay (ms).
setText(id: str, text: str)
: Set the text content of an element.
showModal(id: str)
,
hideModal(id: str)
: Modal visibility helpers.
goTo(href: str)
,
goToNew(href: str)
,
reload()
,
goBack()
,
goForward()
: Navigation utilities.
alert(message: str)
,
confirm(message: str, on_ok: str = "", on_cancel: str = "")
,
log(message: str)
: Alert & console utilities.
show(id: str)
,
hide(id: str)
,
toggle(id: str)
,
addClass(id: str, class_name: str)
,
removeClass(id: str, class_name: str)
,
toggleClass(id: str, class_name: str)
: DOM manipulation utilities.
scrollTo(x: int = 0, y: int = 0)
,
scrollToTop()
,
scrollToBottom()
,
scrollToElement(id: str)
: Scroll utilities.
submitForm(form_id: str)
,
resetForm(form_id: str)
,
getValue(input_id: str, target_id: str)
,
clearInput(input_id: str)
: Form utilities.
saveToLocal(key: str, value: str)
,
loadFromLocal(key: str, target_id: str)
,
removeFromLocal(key: str)
,
clearLocalStorage()
: Storage utilities.
copyToClipboard(text: str)
,
copyElementText(id: str)
: Clipboard utilities.
focus(id: str)
,
blur(id: str)
: Focus utilities.
These functions are documented in
https://ztamdev.github.io/Dars-Framework/docs.html#dars-script-system
and for states in
https://ztamdev.github.io/Dars-Framework/docs.html#state-management-in-dars-dstate-cstate-goto-mods
.
SEO and Apple device optimizations. Enhanced metadata generation with automatic MIME type detection for favicons and comprehensive iOS/Safari support.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.1
Smart Icon Type Recognition:
image/x-icon
type for PNG files
Before (v1.4.0):
<link rel="icon" href="logo.png" type="image/x-icon" />
<!-- Incorrect! -->
After (v1.4.1):
<link rel="icon" href="logo.png" type="image/png" />
<!-- Correct! -->
New App Properties:
apple_mobile_web_app_capable
- Enable fullscreen mode when added to home screen
apple_mobile_web_app_status_bar_style
- Control status bar appearance:
"default"
- Standard iOS status bar
"black"
- Black status bar
"black-translucent"
-
Transparent status bar
(solves Safari iOS 16+ solid color issue)
apple_mobile_web_app_title
- Custom title for home screen icon
Usage:
app = App(
title="My App",
favicon="logo.png",
apple_touch_icon="logo.png",
apple_mobile_web_app_capable=True,
apple_mobile_web_app_status_bar_style="black-translucent", # Enables transparency!
apple_mobile_web_app_title="MyApp"
)
Adaptive Theme Colors:
Generated Meta Tags:
<meta name="theme-color" content="#0d1513" />
<meta
name="theme-color"
media="(prefers-color-scheme: light)"
content="#0d1513"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#0d1513"
/>
Multiple Size Specifications:
Generated Links:
<link rel="apple-touch-icon" href="logo.png" />
<link rel="apple-touch-icon" sizes="180x180" href="logo.png" />
Standards Compliance:
mobile-web-app-capable
meta tag alongside
apple-mobile-web-app-capable
Generated Meta Tags:
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
dars/exporters/web/html_css_js.py
)
_detect_icon_mime_type()
Helper
: Automatically detects MIME types from file extensions
_generate_meta_tags()
: Adds Apple-specific meta tags and theme-color variants
_generate_links()
: Applies automatic MIME detection and sizes attribute
dars/core/app.py
)
apple_mobile_web_app_title
defaults to app title)
Automatic Upgrade:
Optional iOS Enhancement:
# Add to your App initialization
app = App(
# ... existing params ...
apple_mobile_web_app_capable=True,
apple_mobile_web_app_status_bar_style="black-translucent",
apple_mobile_web_app_title="MyApp"
)
Still in BETA - No changes from v1.4.0
Upgrade Highly Recommended for all projects, especially those targeting iOS/Safari users or requiring proper favicon MIME types.
Simple Template update.
pip install --upgrade dars-framework
or
pip install dars-framework==1.4.0
All advanced and basic templates are now updated using the new features.
Desktop File System API, Pythonic Arg helper, keyboard event filtering, and comprehensive template synchronization. Major enhancements to desktop capabilities with improved developer experience.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.9
list_directory
Comprehensive Directory Listing:
list_directory()
function for browsing files and folders
"*.py"
for Python files only)
include_size
parameter (default: False) to show/hide file sizes
get_value()
for dynamic paths from inputs
dScript.then()
for UI updates
{name, isDirectory, size?}
objects
Usage:
from dars.desktop import list_directory, get_value
from dars.core.state import this
# Simple directory listing
Button("List",
on_click=list_directory(get_value("path")).then(
this().state(id="output", html=RawJS("value.map(f => f.name).join('<br>')"))
)
)
# Filter by pattern
Button("Python Files",
on_click=list_directory(".", "*.py").then(
this().state(id="count", text=RawJS("`Found ${value.length} files`"))
)
)
# Include file sizes
list_directory(".", "*", include_size=True)
Arg
Helper
Cleaner dScript.ARG Access:
Arg
singleton for Pythonic access to
dScript.ARG
RawJS("dScript.ARG")
.map()
,
.join()
,
.length
, etc.
dars.all
for easy access
Usage:
from dars.scripts.dscript import Arg
# Old way (still works)
this().state(text=RawJS("dScript.ARG.map(f => f.name).join('\\n')"))
# New Pythonic way
this().state(text=Arg.map("f => f.name").join("\\n"))
# Properties
Arg.length # -> "dScript.ARG.length"
Arg.value # -> "dScript.ARG.value"
# Methods
Arg.map("f => f.name") # -> "dScript.ARG.map(f => f.name)"
Arg.filter("x => x > 0") # -> "dScript.ARG.filter(x => x > 0)"
Specific Key Event Handlers:
KEY_DOWN_ENTER
,
KEY_DOWN_ESCAPE
)
.
delimiter for key filtering (e.g.,
"keydown.Enter"
)
New Event Constants:
from dars.core.events import EventTypes
# Specific Enter key events
on_keydown_enter # Triggered only when Enter is pressed
on_keyup_enter # Triggered only when Enter is released
# Specific Escape key events
on_keydown_escape # Triggered only when Escape is pressed
on_keyup_escape # Triggered only when Escape is released
# Usage
Input(
id="search",
on_keydown_enter=search_action, # Only fires on Enter
on_keydown_escape=clear_action # Only fires on Escape
)
Electron DevTools Control:
devtools
parameter in
App
class (default:
True
)
DARS_DEV
and
DARS_DEVTOOLS
environment variables
Usage:
# Disable DevTools even in dev mode
app = App(
title="My App",
desktop=True,
devtools=False # Won't open DevTools
)
# Default behavior (DevTools enabled)
app = App(
title="My App",
desktop=True
)
Fixed
change
Function:
.value
for
Input
,
Textarea
,
Select
.textContent
for other elements (Text, Button, etc.)
Keyboard Event Fix:
keydown.Enter
and similar events work correctly
change
function to use
.value
for form elements instead of
.textContent
keydown.Enter
Arg
helper for dScript.ARG access
No breaking changes. You can upgrade safely:
pip install --upgrade dars-framework
Optional: Use new features
list_directory
for file browsing:
from dars.desktop import list_directory, get_value
Button("Browse",
on_click=list_directory(get_value("dir")).then(...)
)
Arg
helper for cleaner code:
from dars.scripts.dscript import Arg
# Instead of RawJS("dScript.ARG.map(...)")
text=Arg.map("x => x.name").join("\\n")
Input(
on_keydown_enter=submit_action,
on_keydown_escape=cancel_action
)
Arg
helper documentation in scripts section
Dynamic state updates, improved event handling, and enhanced Electron dev experience. Introduces
this()for event-time component updates,RawJSfor JavaScript injection, anddScript.then()for async chaining.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.8
this()
Effortless Component Updates:
this()
helper for direct, event-time component updates without pre-registering states
text
,
html
,
style
,
attrs
,
classes
Usage:
from dars.core.state import this
# Button that updates itself
btn = Button("Click me", on_click=this().state(
text="Clicked!",
style={"color": "red"}
))
# Counter with Mod helpers
counter = Text("0", id="count")
inc_btn = Button("+1", on_click=this().state(text=Mod.inc("count")))
RawJS
Dynamic Value Passing:
RawJS
class for injecting raw JavaScript variables into state updates
dScript.ARG
as a placeholder for chained script results
Usage:
from dars.scripts.dscript import RawJS, dScript
from dars.desktop import read_text
# File content becomes button text
read_btn = Button("Load",
on_click=read_text("data.txt").then(
this().state(text=RawJS(dScript.ARG))
)
)
dScript.then()
Sequential Async Operations:
.then()
method for chaining
dScript
objects
dScript.ARG
(resolves to
value
)
Usage:
# Chain file operations
Button("Process",
on_click=read_text("input.txt")
.then(dScript(code="return value.toUpperCase()"))
.then(write_text("output.txt", RawJS("value")))
.then(this().state(text="Done!"))
)
Fixed Critical Bug:
btn.on_click = handler
Button(on_click=handler)
__setattr__
override to properly register events in all cases
Improved Development Experience:
Better Logging:
change({dynamic: true, ...})
path in
dars/js_lib.py
RawJS
values
value
variable for inter-script communication
VDomBuilder
now correctly serializes all event handlers
__setattr__
catches
on_*
assignments
restart_triggered
flag handling
Comprehensive Coverage:
this()
section in README with quick examples
state_management.md
with complete file operations guide
exporters.md
with desktop API examples and chaining patterns
scripts.md
with
.then()
method documentation
Seamless Upgrade:
Still in BETA:
read_text
,
write_text
,
read_file
,
write_file
) are stable
Upgrade Highly Recommended for all desktop projects and applications requiring dynamic, event-driven UI updates.
Compile-time and runtime component manipulation. Introduces
app.create()/app.delete()for pre-export tree editing, andcreateComp()/deleteComp()for dynamic DOM operations with safe event hydration.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.6
app.create(target, root=None, on_top_of=None, on_bottom_of=None)
root
.
target
as an instance, a callable, or a
str
(id to move an existing component).
root
can be a component id, a component instance, or a page name.
app.delete(id)
createComp(target, root, position='append')
dScript
that calls
Dars.runtime.createComponent(root_id, vdom_data, position)
.
append
,
prepend
,
before:<id>
,
after:<id>
.
deleteComp(id)
dScript
that calls
Dars.runtime.deleteComponent(id)
.
id
also gets
class="dars-id-<id>"
for reliable selection when multiple instances exist.
from dars.all import *
now includes
createComp
and
deleteComp
from
dars.backend
.
Enhanced styling and development experience update featuring active styles, improved preview system, and advanced file monitoring. Delivers better visual feedback and faster development workflow.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.5
Complete Component Styling System:
active_style
attribute for all components, providing visual feedback during user interaction
style
,
hover_style
, and
active_style
Usage:
Button("Click it",
id="btn1",
on_click=[dScript('console.log(`HI`)'), txtstate.state(1)],
style={"color": "green"},
hover_style={"color": "red"}, # from v1.3.4
active_style={"color": "purple"}, # new in v1.3.5
)
Optimized Development Server:
Comprehensive Project Monitoring:
Seamless Upgrade:
Still in BETA
Enhanced interactivity update featuring hover styles, multi-handler events, and runtime versioning. Improves component styling and event handling flexibility.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.4
Enhanced Component Interactivity:
hover_style
attribute for all components, allowing dynamic styling on mouse hover
Flexible Event Management:
dScript
, inline JavaScript, or mixed handlers can be assigned to single events
Enhanced Debugging & Tracking:
Dars.version
Dars.releaseUrl
for quick reference
Usage:
// Access version information
console.log(`Using Dars v${Dars.version}`);
console.log(`Release: ${Dars.releaseUrl}`);
Automatic Upgrade:
Still in BETA - No changes from v1.3.3
Upgrade Recommended for all projects requiring enhanced interactivity and better development tooling.
Minor update featuring new semantic Section component and enhanced state serialization. Continues JavaScript migration improvements from v1.3.2.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.3
Semantic HTML Container:
Section
component that renders as
<section></section>
instead of generic
<div>
Usage:
# Creates <section> with all container capabilities
Section(Button("HI"), styles={...})
Complete JavaScript Migration:
Benefits:
Automatic Upgrade:
Optional Section Component Adoption:
# Old way (still works)
Container(children=[...])
# New semantic way
Section(children=[...])
Still in BETA - No changes from v1.3.2
Major minification improvements with combined JS files and optimized event handling. Electron desktop exporter remains in beta.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.2
Combined JavaScript Bundles:
viteMinify: true
and
bundle: true
are enabled, the exporter now combines all JavaScript files into single optimized bundles
app.js
app_{slug}.js
bundle
Optimized Event Handling:
Smart File Management:
runtime_dars.js
,
script.js
,
vdom_tree.js
) are not generated
viteMinify: false
{
"viteMinify": true,
"bundle": true,
"defaultMinify": true
}
Desktop Exporter remains in BETA
While the web exporter is now stable and production-ready, the Electron desktop exporter continues in beta due to:
Current Desktop Capabilities:
read_text
,
write_text
)
No breaking changes - existing configurations continue to work. To benefit from the new minification:
dars.config.json
:
{
"viteMinify": true,
"bundle": true
}
dars build
or
dars export
as usual
We're working on:
Native desktop functions and DX improved with dev mode and file system integration.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.1
You can acces native functions via
dars.desktop
module:
from dars.desktop import *
With this module you can acces for now 2 main functions:
write_text("./app/lib/hello.txt", "Hello Text")
and
read_text("./app/lib/hello.txt")
This two functions allows you to read and write text files in your desktop application filesystem, both returns an dScript() with the code to be executed in the desktop app, and also the 2 functions can be used in events of any component.
Also you can use dScripts to run custom javascript code in the desktop app. and for now 'dars dev' is supported but python main.py with rTimeCompile() is not supported because it have issues with relative paths.
Native desktop export (BETA), improved CLI and doctor integration, and metadata fixes for packaging. This is a BETA release: usable for testing, not recommended for production.
pip install --upgrade dars-framework
or
pip install dars-framework==1.3.0
New: Native Desktop Export (BETA)
format: "desktop"
and
targetPlatform
(
auto|windows|linux|macos
).
dars init --type desktop
(or
--update
), generating minimal main process files and preload bridge.
dist/source-electron/
; packaged artifacts in
dist/
.
New: Desktop Build Flow in CLI
dars build
respects
format: "desktop"
in
dars.config.json
.
targetPlatform
.
New: Doctor Integration for Desktop Tooling
dars doctor --all --yes
checks and installs optional desktop tooling.
Packaging Reliability Improvements
App
(name/title, description, author, version) with sensible defaults.
0.1.0
when absent (warning emitted).
# Initialize or update a project with desktop scaffolding
dars init --type desktop
# or
dars init --update
# Verify optional tooling
dars doctor --all --yes
# Build using project config (format: "desktop")
dars build
Minimal
dars.config.json
for desktop:
{
"entry": "main.py",
"format": "desktop",
"outdir": "dist",
"targetPlatform": "auto"
}
description
from
App.description
(fallback to a default)
author
from
App.author
(fallback to a default)
version
from
App.version
(fallback
0.1.0
with warning)
--win
,
--linux
,
--mac
.
I spent ~1 month iterating on this capability and consolidated changes into this single BETA release once it reached a usable threshold. Feedback is welcome to help stabilize and expand the desktop feature set.
Optional default minification, precise CLI control, faster builds, and clearer minification status.
pip install --upgrade dars-framework
or
pip install dars-framework==1.2.8
New: Configurable Default Minifier
defaultMinify
(default: true) controls the built‑in Python minifier.
pre
,
code
,
textarea
,
script
,
style
.
<pre><code>
remain intact).
New: CLI Flag
--no-minify
dars build
and
dars export
.
viteMinify
.
Minification Modes Work Together
defaultMinify
and
viteMinify
are both true:
Faster Default Minifier
defaultMinify
and
--no-minify
are strictly honored.
defaultMinify
: true/false. Controls the Python-side minification (HTML + JS/CSS fallback).
viteMinify
: true/false. Controls Vite/esbuild for JS/CSS when available.
--no-minify
without affecting
viteMinify
.
Upgrade Recommendation: Recommended for all users; especially helpful for projects with Markdown/code samples and those wanting faster, more controllable minification.
In the github repository Here .