Installation Guide - Dars Framework

Quick Installation

To install Dars, simply use pip:

pip install dars-framework

This will install Dars and all its dependencies automatically.

VS Code Extension

You can install the official Dars Framework VS Code extension to have the dars dev tools.

  • VS Code Marketplace : https://marketplace.visualstudio.com/items?itemName=ZtaMDev.dars-framework
  • Open VSX : https://open-vsx.org/extension/ztamdev/dars-framework

CLI Usage

Once installed, the dars command will be available in your terminal. You can use it to:

Export Applications

dars export my_app.py --format html --output ./my_app_web

Preview Applications

dars preview ./my_app_web

Initialize a New Project

# Basic project with Hello World
dars init my_new_project

# Project with a specific template
dars init my_project -t basic/Forms

View Application Information

dars info my_app.py

View Supported Formats

dars formats

Post-Installation Verification

To verify that Dars has been installed correctly, open your terminal and run:

dars --help

You should see the help for the dars command, indicating that the installation was successful.

First Steps After Installation

1. Create Your First Application (my_first_app.py)

from dars.core.app import App
from dars.components.basic.text import Text
from dars.components.basic.container import Container

app = App(title="My First App")
container = Container(style={'padding': '20px'}) # Use ' to escape quotes
text = Text(text="Hello Dars!", style={'font-size': '24px'}) # Use ' to escape quotes

container.add_child(text)
app.set_root(container)

if __name__ == "__main__":
    app.rTimeCompile()

2. Export the Application

Save the code above as my_first_app.py and then run:

dars export my_first_app.py --format html --output ./my_app

3. Preview

dars preview ./my_app

Useful Commands

# General help
dars --help

# Application information
dars info my_app.py

# Available formats
dars formats

# Preview application
dars preview ./output_directory

Post-Installation Checklist

  • [x] Python 3.8+ installed
  • [x] Dars Framework installed via pip install dars-framework
  • [x] CLI dars works correctly ( dars --help )
  • [x] Basic test run successfully
  • [x] Example exported and previewed correctly
  • [x] Documentation reviewed

Congratulations! Dars is ready to use.


Desktop (BETA)

You can build native desktop apps from Dars projects. This capability is in BETA and is not recommended for production yet, but it is usable for testing.

Quickstart

# Scaffold or update a desktop-capable project
dars init --type desktop
# or
dars init --update

# Verify optional tooling (Node/Bun and packager)
dars doctor --all --yes

# Ensure your config sets the desktop format and target
# dars.config.json
{
  "entry": "main.py",
  "format": "desktop",
  "outdir": "dist",
  "targetPlatform": "auto"
}

# Build desktop artifacts
dars build

Notes: - Desktop support is under active development; configuration keys and defaults may change. - Some platform targets (like macOS) require building on that OS for signing.

Getting Started with Dars

Welcome to Dars, a modern Python framework for building web applications with reusable UI components.

VS Code Extension

There is an official Dars Framework extension for VS Code to have the dars dev tools.

  • VS Code Marketplace : https://marketplace.visualstudio.com/items?itemName=ZtaMDev.dars-framework
  • Open VSX : https://open-vsx.org/extension/ztamdev/dars-framework

Quick Start

  1. Install Dars
    See INSTALL section for installation instructions.

  2. Explore Components
    Discover all available UI components in components.md .

  3. Command-Line Usage
    Find CLI commands, options, and workflows in cli.md .

  4. App Class Learn how to create an app class in App Documentation .

  5. Component Search and Modification All components in Dars now support a powerful search and modification system:

from dars.all import *

app = App(title="Hello World", theme="dark")

# 1. Define State
state = State("app", title_val="Simple Counter", count=0)

# 2. Define Route
@route("/")
def index(): 
    return Page(
        # 3. Use useValue for app text
        Text(
            text=useValue("app.title_val"),
            style="fs-[33px] text-black font-bold mb-[5x] ",
        ),

        # 4. Display reactive count
        Text(
            text=useDynamic("app.count"),
            style="fs-[48px] mt-5 mb-[12px]"
        ),
        # 5. Interactive Button
        Button(
            text="+1",
            on_click=(
                state.count.increment(1)
            ),
            style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] border-none cursor-pointer fs-[18px]",
        ),

        # 6. Interactive Button
        Button(
            text="-1",
            on_click=(
                state.count.decrement(1)
            ),
            style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] border-none cursor-pointer fs-[18px] mt-[5px]",
        ),
        # 7. Interactive Button
        Button(
            text="Reset",
            on_click=(
                state.reset()
            ),
            style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] border-none cursor-pointer fs-[18px] mt-[5px]",
        ),
        style="flex flex-col items-center justify-center h-[100vh] ffam-[Arial] bg-[#f0f2f5]",

    ) 

# 8. Add page
app.add_page("index", index(), title="index")

# 9. Run app with preview
if __name__ == "__main__":
    app.rTimeCompile()
  1. Adding Custom File Types
app.rTimeCompile().add_file_types = ".js,.css"
  • Include any extension your project uses beyond default Python files.

Need More Help?

  • For advanced topics, see the full documentation and examples in the referenced files above.
  • If you have questions or need support, check the official repository or community channels.

Start building with Dars...

Dars CLI Reference

The Dars Command Line Interface (CLI) lets you manage your projects, export apps, and preview results quickly from the terminal.

How to Use the CLI

Open your terminal in your project directory and use any of the following commands:

# Show information about your app
 dars info my_app.py

# Export to different formats (web)
 dars export my_app.py --format html --output ./output
 # Skip default Python minifier for this run (does not affect viteMinify)
 dars export my_app.py --format html --output ./output --no-minify

# List supported export formats
 dars formats

# Initialize a new project (Default is SPA)
 dars init my_new_project

# Initialize a Full-Stack SSR project
 dars init my_new_project --type ssr

# Initialize a project with a specific template
 dars init my_new_project -t demo/complete_app

# Preview an exported app
 dars preview ./output_directory

# Build using project config (dars.config.json)
 dars build
 # Build desktop (BETA) when format is desktop in config
 dars build
 # Build without the default Python minifier
 dars build --no-minify

# Help
 dars --help

# Version
 dars -v

Main Commands Table

Command What it does
dars export my_app.py --format html Export app to HTML/CSS/JS in ./my_app_web
dars export my_app.py --format html --no-minify Export skipping default Python minifier
dars preview ./my_app_web Preview exported app locally
dars build Build using dars.config.json
dars init --type desktop Scaffold desktop-capable project (BETA)
dars build (desktop config) Build desktop app artifacts (BETA)
dars build --no-minify Build skipping default Python minifier
dars init my_project --type ssr Create a new Full-Stack SSR project
dars init my_project Create a new Dars project (SPA Default)
dars dev Run the configured entry file with hot preview (app.rTimeCompile)
dars dev --backend Run only the configured backendEntry (FastAPI/SSR backend)
dars info my_app.py Show info about your app
dars formats List supported export formats
dars --help Show help and all CLI options

Using Official Templates

Dars provides official templates to help you start new projects quickly. Templates include ready-to-use apps for forms, layouts, dashboards, multipage, and more.

How to Use a Template

  1. Initialize a new project with a template:
    dars init my_new_project -t basic/HelloWorld
    # ...and more (see below)
    

You can see the templates available with

dars init --list-templates
dars init  -L
  1. Export the template to HTML/CSS/JS:

    dars export main.py --format html --output ./hello_output
    dars export main.py --format html --output ./dashboard_output
    # ...etc
    
  2. Preview the exported app:

    dars preview ./hello_output
    

Tips CLI

  • Use dars --help for a full list of commands and options.
  • You can preview apps either live (with app.rTimeCompile() ) or from exported files with dars preview .
  • Templates are available for quick project setup: use dars init my_project -t <template> .

Desktop (BETA) CLI

  • Mark your project with "format": "desktop" in dars.config.json .
  • Use dars init --type desktop (or --update ) to scaffold backend files.
  • Run dars doctor --all --yes to set up optional tooling.
  • Build with dars build . This feature is in BETA: suitable for testing, not yet for production.

Minification labels in output

  • Applying minification (default): default Python-side minifier is active.
  • Applying minification (vite): Vite/esbuild minification is active (JS/CSS) and default is disabled.
  • Applying minification (default + vite): both are active.

For more, see the Getting Started guide and the main documentation index.

SSR Workflow (Full-Stack)

When working with SSR ( dars init --type ssr ), the workflow involves two processes:

  1. Frontend ( dars dev ) : Runs the Dars preview server on port 8000 using app.rTimeCompile() .
  2. Backend ( dars dev --backend ) : Runs the FastAPI SSR backend on port 3000 using backendEntry from dars.config.json .

Common Commands:

Command Description
dars init --type ssr Scaffolds a project with backend/ folder and SSR config.
dars dev Starts the hot-reload frontend preview server.
dars dev --backend Starts the SSR/backend server defined by backendEntry .
dars build Builds static assets to dist/ for production.

Note: For production, you only need to run the backend (which serves the built assets).

Dars Project Configuration

The file (dars.config.json) configures how Dars exports and builds your project. It is created by dars init <name> for new projects and can be merged/updated in existing projects with dars init --update .

Example

{
  "entry": "main.py",
  "format": "html",
  "outdir": "dist",
  "publicDir": null,
  "include": [],
  "exclude": ["**/__pycache__", ".git", ".venv", "node_modules"],
  "bundle": false,
  "defaultMinify": true,
  "viteMinify": true,
  "markdownHighlight": true,
  "markdownHighlightTheme": "auto",
  "utility_styles": {},
  "backendEntry": "backend.api:app"
}

Fields

  • entry Python entry file for your app. Used by dars build and by dars export config .

  • format Export format. Supported: html and desktop (BETA). When set to desktop , the build command will produce native desktop artifacts.

  • outdir Directory where the exported files are written.

  • publicDir Directory whose contents are copied as-is into the output (e.g. public/ or assets/ ). If null , Dars will try to autodetect common locations.

  • include / exclude Simple filters (by substring) applied when copying from publicDir .

  • bundle Reserved for future use. Current exporters already produce a bundled output.

  • defaultMinify Toggle the built-in Python minifier (safe and conservative). Controls HTML minification and provides JS/CSS fallback when advanced tools are unavailable.

    • true (default): run the default Python-side minifier.
    • false : skip the default minifier. You can still use Vite/esbuild via viteMinify .
  • viteMinify Toggle the advanced JS minifier.

    • true (default): prefer the advanced minifier; fall back to the secondary minifier; if neither is available, a conservative built-in fallback is used.
    • false : skip the advanced minifier and use the secondary minifier directly; fall back to the conservative built-in if not available.
  • utility_styles Dictionary defining custom utility classes. Keys are class names, values are lists of utility strings or raw CSS properties. Example: "btn-primary": ["bg-blue-500", "text-white"]

  • markdownHighlight Auto-inject a client-side syntax highlighter for fenced code blocks in Markdown.

    • true (default): injects Prism.js assets once per page and highlights pre code blocks.
    • false : no assets injected; you can include your own highlighter or none at all.
  • backendEntry Python import path for your FastAPI/SSR backend application (e.g. "backend.api:app" ).

    • Used by tools like dars dev --backend to start the backend server.
    • When your app defines routes with RouteType.SSR , dars config validate will require this field to be present.

Desktop-specific (BETA)

  • targetPlatform Desktop build target. Only effective when format is desktop .
    • Values: auto (default), windows , linux , macos .
    • Note: macOS targets must be built on macOS for signing.

Desktop export is BETA: suitable for testing, not recommended for production yet. Configuration keys and defaults may change.

Behavior and defaults

  • dars init --update merges your existing config with Dars defaults and writes the result back, adding any new keys (like defaultMinify , viteMinify ) without removing your current settings.
  • During dars export and dars build , Dars reads this file and configures the minification pipeline accordingly.
  • If advanced minifiers are not available, builds still complete with a conservative fallback. On dars build , a small notice may appear indicating that a less powerful minifier was used.
  • You can force-skip the default Python minifier per run with --no-minify (does not affect viteMinify ).

Tips

  • To add or refresh the config in an existing project:

    dars init --update
    
  • To review optional tooling that can enhance bundling/minification, run:

    dars doctor
    
  • If you want to force using only the secondary minifier, set "viteMinify": false .

    • To disable the default minifier by config, set "defaultMinify": false ; to disable it per-run use --no-minify .

Environment Management (DarsEnv)

Dars provides a simple, built-in way to detect the current running environment (Development vs Production) through the DarsEnv class.

Why use DarsEnv?

It is common to have logic that should only run during development (like showing debug tools, detailed logs, or specific navigation links) or only in production (like analytics scripts).

DarsEnv Class

The DarsEnv class is available directly from dars.env :

from dars.env import DarsEnv

Properties

DarsEnv.dev

A boolean property that indicates if the application is running in development mode.

  • Returns True : When running via dars dev (where bundling is disabled by default).
  • Returns False : When running via dars build or dars export (production builds).

Examples

Conditional Rendering

You can use DarsEnv.dev to conditionally render components:

from dars.all import *
from dars.env import DarsEnv

def MyPage():
    return Page(
        Container(
            Text("Welcome to My App"),

            # This link only appears in development
            Link("/debug-dashboard", "Debug Tools") if DarsEnv.dev else None,

            # Different footer for environments
            Text("Dev Mode: Active" if DarsEnv.dev else "Production Build")
        )
    )

Conditional Configuration

You can also use it to toggle configuration values:

api_url = "http://localhost:3000" if DarsEnv.dev else "https://api.myapp.com"

Note : DarsEnv is initialized automatically by the Dars CLI tools. You do not need to configure it manually.

App Class and PWA Features in Dars Framework

Overview

The App class is the core of any Dars Framework application. It represents the complete application and manages all configuration, components, pages, and functionalities, including Progressive Web App (PWA) support.

Basic Structure

class App:
    def __init__(
        self, 
        title: str = "Dars App",
        description: str = "",
        author: str = "",
        keywords: List[str] = None,
        language: str = "en",
        favicon: str = "",
        icon: str = "",
        apple_touch_icon: str = "",
        manifest: str = "",
        theme_color: str = "#000000",
        background_color: str = "#ffffff",
        service_worker_path: str = "",
        service_worker_enabled: bool = False,
        **config
    ):

Compile-Time Component Manipulation (App.create / App.delete)

Dars lets you modify the component tree before export or preview. Use these methods on App to insert or remove components safely at compile time.

App.create(target, root=None, on_top_of=None, on_bottom_of=None)

  • Purpose : Insert a component into the tree.
  • target can be:
    • A component instance (e.g., Button("OK", id="ok") ).
    • A callable that returns an instance (called lazily).
    • A str id of an existing component in the app tree to move it.
  • root : Where to insert. Accepts:
    • A component instance.
    • A component id ( str ).
    • A page name ( str ) in multipage apps.
  • on_top_of / on_bottom_of : Reference sibling inside root to place the new node before/after. Can be an id ( str ) or a component instance found within root (deep search). If neither is provided, it appends to root .
  • Works with both single-page ( app.root ) and multipage ( app.add_page ) setups.
  • If anything can’t be resolved, it fails gracefully without crashing.

Examples

from dars.all import *

app = App(title="Compile-time create/delete")

# Single-page usage
root = Container(Text("A" , id="a"), Text("C", id="c"), id="root")
app.set_root(root)

# Insert new Text before id="c"
app.create(Text("B", id="b"), root="root", on_top_of="c")

# Move existing component by id to bottom
app.create("a", root="root", on_bottom_of="c")

# Multipage usage (root by page name)
home = Page(Container(Text("Home"), id="home_root"))
app.add_page(name="home", root=home, index=True)
app.create(Text("Welcome", id="welcome"), root="home", on_bottom_of="home_root")

App.delete(id)

  • Purpose : Remove a component from the tree by its id .
  • If the id does not exist, it becomes a no-op (safe warning behavior).
# Remove a component by id before export/preview
app.delete("b")

These operations run before export and affect the generated HTML/VDOM. For dynamic changes at runtime in the browser, see the runtime APIs in the Components documentation.

Global Styles Management

Enhanced add_global_style() Method

The App class now includes an enhanced add_global_style() method that supports both inline style definitions and external CSS file imports.

Usage Examples

1. Inline Style Definition (Traditional)

app.add_global_style(
    selector=".my-button",
    styles={
        "background-color": "#4CAF50",
        "color": "white",
        "padding": "10px 20px",
        "border-radius": "5px"
    }
)

2. External CSS File Import (New in v1.1.2)

app.add_global_style(file_path="styles.css")

3. Combined Usage

# Add inline styles
app.add_global_style(
    selector=".primary-btn",
    styles={
        "background-color": "#007bff",
        "color": "white"
    }
)

# Import external CSS file
app.add_global_style(file_path="components.css")

Practical Example with External CSS

main.py:

from dars.all import *

app = App(title="Dars Styling Test")

index = Page(
    Container(
        Button("Styled Button", class_name="button-styling-test"),
        id="page_sub_container"
    )
)

app.add_page(name="index", root=index, title="index", index=True)
app.add_global_style(file_path="styles.css")

if __name__ == "__main__":
    app.rTimeCompile(add_file_types=".py, .css")

styles.css:

.button-styling-test {
    background-color: rgb(51, 255, 0);
    padding: 15px 30px;
    border-radius: 8px;
    border: none;
    font-weight: bold;
    cursor: pointer;
}

.button-styling-test:hover {
    background-color: rgb(30, 200, 0);
    transform: scale(1.05);
}

#page_sub_container {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
}

Hot Reload for CSS Files

When using app.rTimeCompile() with the add_file_types=".css" parameter, the development server automatically watches for changes in CSS files and reloads the application when modifications are detected.

# Watch for both Python and CSS file changes
app.rTimeCompile(add_file_types=".py, .css")

# Or watch for CSS files only
app.rTimeCompile(add_file_types=".css")

Benefits of External CSS Import

  1. Separation of Concerns : Keep styles separate from application logic
  2. Better Organization : Maintain complex stylesheets in dedicated files
  3. Team Collaboration : Designers and developers can work simultaneously
  4. CSS Preprocessors : Use SASS, LESS, or other preprocessors
  5. Performance : Browser caching for external CSS files

Backward Compatibility

The enhanced method maintains full backward compatibility - existing code using add_global_style(selector, styles) will continue to work without modification.

PWA Configuration Properties

The App class includes these PWA-specific properties:

# Icons and visual resources
self.favicon = favicon  # Path to traditional favicon
self.icon = icon  # Main icon for PWA (multiple sizes)
self.apple_touch_icon = apple_touch_icon  # Icon for Apple devices
self.manifest = manifest  # Path to manifest.json file

# Colors and theme
self.theme_color = theme_color  # Theme color (#RRGGBB)
self.background_color = background_color  # Background color for splash screens

# Service Worker
self.service_worker_path = service_worker_path  # Path to service worker file
self.service_worker_enabled = service_worker_enabled  # Enable/disable

# Additional PWA configuration
self.pwa_enabled = config.get('pwa_enabled', False)
self.pwa_name = config.get('pwa_name', title)
self.pwa_short_name = config.get('pwa_short_name', title[:12])
self.pwa_display = config.get('pwa_display', 'standalone')
self.pwa_orientation = config.get('pwa_orientation', 'portrait')

Meta Tag Generation for PWA

The App class provides methods to generate PWA meta tags:

def get_meta_tags(self) -> Dict[str, str]:
    """Returns all meta tags as a dictionary"""
    meta_tags = {}

    # Viewport configured for responsiveness
    viewport_parts = []
    for key, value in self.config['viewport'].items():
        if key == 'initial_scale':
            viewport_parts.append(f'initial-scale={value}')
        elif key == 'user_scalable':
            viewport_parts.append(f'user-scalable={value}')
        else:
            viewport_parts.append(f'{key.replace("_", "-")}={value}')
    meta_tags['viewport'] = ', '.join(viewport_parts)

    # Specific tags for PWA
    meta_tags['theme-color'] = self.theme_color
    if self.pwa_enabled:
        meta_tags['mobile-web-app-capable'] = 'yes'
        meta_tags['apple-mobile-web-app-capable'] = 'yes'
        meta_tags['apple-mobile-web-app-status-bar-style'] = 'default'
        meta_tags['apple-mobile-web-app-title'] = self.pwa_short_name

    return meta_tags

Integration with HTML/CSS/JS Exporter

The HTMLCSSJSExporter uses the PWA configuration from the App class to generate:

  1. manifest.json file - Progressive web app configuration
  2. Meta tags - To indicate PWA capabilities in different browsers
  3. Icon references - For multiple devices and sizes
  4. Service Worker registration - For offline functionality

Example of Generated Manifest.json

{
  "name": "App Name",
  "short_name": "Short Name",
  "description": "Application description",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "orientation": "portrait",
  "icons": [
    {
      "src": "icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Service Worker Registration Script

The exporter automatically generates code to register the service worker:

if ('serviceWorker' in navigator && '{service_worker_path}') {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('{service_worker_path}')
      .then(function(registration) {
        console.log('ServiceWorker registration successful');
      })
      .catch(function(error) {
        console.log('ServiceWorker registration failed: ', error);
      });
  });
}

Complete PWA App Configuration Example

# Create a complete PWA application
app = App(
    title="My PWA App",
    description="An amazing progressive application",
    author="My Company",
    keywords=["pwa", "webapp", "productivity"],
    language="en",
    favicon="assets/favicon.ico",
    icon="assets/icon-192x192.png",
    apple_touch_icon="assets/apple-touch-icon.png",
    theme_color="#4A90E2",
    background_color="#FFFFFF",
    service_worker_path="sw.js",
    service_worker_enabled=True,
    pwa_enabled=True,
    pwa_name="My App",
    pwa_short_name="MyApp",
    pwa_display="standalone"
)

# Add pages and components
app.add_page("home", HomeComponent(), title="Home", index=True)
app.add_page("about", AboutComponent(), title="About")

Implementation Considerations

Browser Compatibility

Dars Framework's PWA implementation is compatible with: - Chrome/Chromium (full support) - Firefox (basic support) - Safari (limited support on iOS) - Edge (full support)

Dars - Components Documentation


Barrel Import

You can now import all main components and modules with a single line:

from dars.all import *

This simplifies integration and improves the developer experience.

But if you crate your own components it can cause conflicts with names of your components and built in components if they have the same name.


Introduction to Components

Components are the fundamental elements of Dars that represent UI elements. Each component encapsulates its appearance, behavior, and state, allowing you to create complex interfaces by composing simple elements.

To learn how to create your own custom components, refer to the documentation in Custom Components .

Event Handling with dScript

Dars provides a powerful way to handle user interactions through the dScript class. You can attach event handlers to interactive components like Button and Input to create dynamic and responsive user interfaces.

For a complete list of available event types and how to use them, refer to the documentation in Events .

Runtime Component Manipulation (createComp / deleteComp)

You can create or delete components dynamically at runtime in the browser using createComp() and deleteComp() . These functions return dScript so you can attach them to events.

  • from dars.all import * exposes createComp and deleteComp .
  • The created subtree is serialized to VDOM, mounted into the DOM, and its events are rehydrated automatically.
  • Every node that has an id also receives a CSS class dars-id-<id> to help target multiple instances when needed.

API

createComp(target, root, position='append') -> dScript
deleteComp(id) -> dScript
  • target : a component instance or a callable returning one.
  • root : id (string) of the DOM/container where to insert.
  • position : where to place the new element inside root .
    • append (default)
    • prepend
    • before:<id> (insert before a reference sibling id)
    • after:<id> (insert after a reference sibling id)

Examples

from dars.all import *

container = Container(id="root")

# Add a button that creates a new Text inside #root on click
add_btn = Button(
    text="Add",
    id="add",
    on_click=createComp(Text("Hi", id="msg"), root="root", position='append')
)

# Add another button that inserts before a specific sibling
insert_btn = Button(
    text="Insert Before",
    on_click=createComp(Text("Before", id="before"), root="root", position='before:msg')
)

# Button that deletes an element by id
delete_btn = Button(
    text="Delete msg",
    on_click=deleteComp("msg")
)

Component Lifecycle Hooks (onMount, onUpdate, onUnmount)

Dars components (including @FunctionComponent ) support lifecycle hooks that run in the browser:

  • onMount : runs once when the component is registered in the runtime and mounted into the DOM.
  • onUpdate : runs after dynamic changes that affect the component, for example:
    • Dars.change({ id, dynamic: true, ... }) (used internally by updateComp ).
    • VRef changes that affect its subtree ( updateVRef() + Dars.updateVRef ).
  • onUnmount : runs right before the component is removed from the DOM (for example, via deleteComp ).

Hooks accept dScript or inline JS strings and are passed as component props.

Basic Usage

from dars.all import *

@route("/")
def index():
    counter_box = Container(
        Text("Dynamic counter: ", id="dyn_label"),
        Text(setVRef(0, ".dyn_count")),
        id="dyn_box",
        class_name="p-4 border rounded mb-2",
        onMount=dScript("console.log('[Lifecycle] dyn_box mounted');"),
        onUpdate=dScript("console.log('[Lifecycle] dyn_box updated');"),
        onUnmount=dScript("console.log('[Lifecycle] dyn_box unmounted');"),
    )

    host_id = "dyn_host"

    return Page(
        Container(
            Container(id=host_id),

            # Dynamically create the component with lifecycle and VRefs
            Button(
                "Create",
                on_click=createComp(counter_box, host_id, position="append"),
            ),

            # Update the value using V() + updateVRef (triggers onUpdate)
            Button(
                "Increment",
                on_click=updateVRef(
                    ".dyn_count",
                    V(".dyn_count").int() + 1,
                ),
            ),

            # Delete the component (triggers onUnmount)
            Button(
                "Delete",
                on_click=deleteComp("dyn_box"),
            ),
        )
    )

Behavior in different flows

  • Initial render / hydration :

    • The VDOM includes a lifecycle block per node with onMount , onUpdate , onUnmount .
    • The runtime registers hooks during DarsHydrate and runs onMount once per id.
  • createComp / deleteComp :

    • createComp uses VDomBuilder to include lifecycle and events in the dynamic VDOM.
    • runtime.createComponent registers lifecycle for the subtree and runs onMount after inserting it into the DOM.
    • deleteComp causes runtime.deleteComponent to run onUnmount before removing the node.
  • Dynamic changes and VRefs :

    • updateComp() generates calls to Dars.change({ id, dynamic: true, ... }) , which eventually call onUpdate for that id.
    • updateVRef(selector, ...) updates DOM and VRefs, then calls Dars.updateVRef(selector) , which finds the nearest lifecycle-enabled ancestor for each affected element and runs its onUpdate .

This allows you to attach side effects (logs, external integrations, controlled side-effects) to your components' lifecycle, both for static components and those created/removed at runtime.


dScript Basic Usage

from dars.all import *

# Button with click handler
button = Button(
    text="Click me",
    on_click=dScript("""
        function handleClick(event) {
            alert('Button was clicked!');
            // Access the button element
            const button = event.target;
            // Toggle a class on click
            button.classList.toggle('clicked');
        }
    """)
)

# Input with change handler
input_field = Input(
    placeholder="Type something...",
    on_change=dScript("""
        function handleChange(event) {
            console.log('Input value changed to:', event.target.value);
            // Add validation or other logic here
            if (event.target.value.length < 3) {
                event.target.style.borderColor = 'red';
            } else {
                event.target.style.borderColor = 'green';
            }
        }
    """
)

Available Events

Component Event Description
Button on_click Triggered when the button is clicked
Button on_double_click Triggered on double click
Button on_mouse_enter Triggered when mouse enters the button
Button on_mouse_leave Triggered when mouse leaves the button
Input on_change Triggered when input value changes
Input on_key_up Triggered when a key is released
Input on_key_press Triggered when a key is pressed

Events and dScriptBest Practices

  1. Use Named Functions : Makes debugging easier and allows reusing the same function for multiple events.
  2. Keep Handlers Small : Move complex logic to separate functions in your JavaScript code.
  3. Access Event Object : The event object provides useful properties like target , keyCode , etc.
  4. Return false to prevent default behavior when needed.

Quick Access


Base Component Class

All components in Dars inherit from the base Component class, which provides common functionality:

from dars.core.component import Component

class Component(ABC):
    def __init__(self, **props):
        self.props = props
        self.children: List[Component] = []
        self.parent: Optional[Component] = None
        self.id: Optional[str] = props.get('id')
        self.class_name: str = props.get("class_name", self.__class__.__name__)
        self.style: Dict[str, Any] = props.get('style', {})
        self.hover_style: Dict[str, Any] = props.get('hover_style', {})
        self.active_style: Dict[str, Any] = props.get('active_style', {})
        self.events: Dict[str, Callable] = {}
        self.key: Optional[str] = props.get('key')

Global Properties

All components support these basic properties:

  • id : Unique component identifier
  • class_name : CSS class for additional styles
  • style : Dictionary of CSS styles
  • hover_style : Dictionary of CSS styles on hover
  • active_style : Dictionary of CSS styles when active
  • set_event(event_name, handler) : Attach event handlers
  • on_click event handler receives a dScript object or a comp.state() function
  • on_double_click event handler receives a dScript object or a comp.state() function
  • on_mouse_down event handler receives a dScript object or a comp.state() function
  • on_mouse_up event handler receives a dScript object or a comp.state() function
  • on_mouse_enter event handler receives a dScript object or a comp.state() function
  • on_mouse_leave event handler receives a dScript object or a comp.state() function
  • on_mouse_move event handler receives a dScript object or a comp.state() function
  • on_key_press event handler receives a dScript object or a comp.state() function
  • on_key_up event handler receives a dScript object or a comp.state() function
  • on_key_press event handler receives a dScript object or a comp.state() function
  • on_change event handler receives a dScript object or a comp.state() function
  • on_input event handler receives a dScript object or a comp.state() function
  • on_submit event handler receives a dScript object or a comp.state() function
  • on_focus event handler receives a dScript object or a comp.state() function
  • on_blur event handler receives a dScript object or a comp.state() function
  • on_load event handler receives a dScript object or a comp.state() function
  • on_error event handler receives a dScript object or a comp.state() function
  • on_resize event handler receives a dScript object or a comp.state() function

  • children : List of child components (for containers)

Component-Search-and-Modification

All components include a powerful search and modification system through the find() method. This allows you to search for components in the component tree and modify their attributes using a fluent interface.

# Find by ID
component.find(id="search-button")

# Find by CSS class
component.find(class_name="primary-button")

# Find by component type
component.find(type="Button")  # or type=Button

# Find using a custom predicate
component.find(predicate=lambda c: "welcome" in c.text.lower())

Chained Searches

You can chain multiple find() calls to search within the results of previous searches:

# Find a container and then search within it
component.find(id="main-container")\
        .find(type="Text")\
        .attr(text="New text")

# Multiple levels of search
component.find(class_name="section")\
        .find(type="Container")\
        .find(id="special-text")\
        .attr(text="Modified text")

Modifying Components

Use the attr() method to modify the found components:

# Modify styles
component.find(type="Button").attr(
    style={"background-color": "red", "color": "white"}
)

# Modify class names
component.find(class_name="btn").attr(
    class_name="btn primary"
)

# Modify component-specific attributes
component.find(type="Text").attr(
    text="New content"
)

# Multiple modifications at once
component.find(type="Input").attr(
    placeholder="Type here...",
    style={"padding": "10px"},
    class_name="modern-input"
)

Getting Results

# Get all matched components
components = component.find(type="Button").get()

# Get only the first match
first_button = component.find(type="Button").first()

Search Parameters

Parameter Type Description Example
id str Search by component ID find(id="search-btn")
class_name str Search by CSS class find(class_name="primary")
type str/Type Search by component type find(type="Button") or find(type=Button)
predicate Callable Custom search function find(predicate=lambda c: len(c.children) > 0)

Page

The Page component represents the root of a multipage app. It can contain other components and scripts specific to that page.

Page Syntax

from dars.components.basic import Page, Text, Button
from dars.scripts.script import InlineScript
page = Page(
    Text("Bienvenido!"),
    Button("Click aquí", id="btn-demo")
)
# Añadir script JS solo a esta página
page.add_script(InlineScript("""
document.addEventListener('DOMContentLoaded', function() {
    var btn = document.getElementById('btn-demo');
    if (btn) btn.onclick = () => alert('¡Botón de esta página!');
});
"""))

Use Page as the root of each page in the multipage system. Allows passing children directly as arguments and JS scripts per page.

Page Properties

Property Type Description
children list List of child components
anchors dict Optional anchor points for child placement

Page Scripts System

The new page scripts system allows assigning scripts to specific pages instead of globally:

  • Adding Scripts : Use the add_script() method on a page instance.
from dars.scripts.dscript import dScript

index.add_script(
    dScript(code="console.log('Hello world')")
)

The Head component allows you to manage the <head> section of your page, including the title, meta tags, and links. It supports SEO metadata like Open Graph and Twitter Cards.

Head Syntax

from dars.components.advanced.head import Head

head = Head(
    title="My Page Title",
    description="This is a description for SEO.",
    keywords="dars, framework, python",
    og_title="My Open Graph Title",
    twitter_card="summary_large_image"
)

Head Properties

Property Type Description
title str The page title ( <title> )
description str Meta description
keywords str/list Meta keywords
author str Meta author
robots str Robots meta tag
canonical str Canonical URL link
favicon str Favicon URL link
og_title str Open Graph title
og_description str Open Graph description
og_image str Open Graph image URL
og_type str Open Graph type (default: "website")
twitter_card str Twitter card type (default: "summary")
twitter_site str Twitter site handle
twitter_creator str Twitter creator handle
meta dict Custom meta tags {name: content}
links list Custom link tags [{rel: ..., href: ...}]
structured_data dict JSON-LD structured data

Text

The Text component displays static or dynamic text.

Text Syntax

from dars.components.basic.text import Text

text = Text(
    text="Contenido del text",
    id="mi-text",
    class_name="text-principal",
    style={
        "font-size": "16px",
        "color": "#333",
        "font-weight": "bold"
    }
)

Text Properties

Property Type Description Example
text str Text content "Hello world"
id str Unique identifier "title-primary"
class_name str CSS class "text-highlight"
style dict CSS styles {"color": "red"}

Text Common Styles

# Título principal
title = Text(
    text="Título Principal",
    style={
        "font-size": "32px",
        "font-weight": "bold",
        "color": "#2c3e50",
        "margin-bottom": "20px",
        "text-align": "center"
    }
)

# Párrafo de contenido
paragraph = Text(
    text="Este es un párrafo de ejemplo con contenido descriptivo.",
    style={
        "font-size": "16px",
        "line-height": "1.6",
        "color": "#34495e",
        "margin-bottom": "15px"
    }
)

# Texto pequeño
note = Text(
    text="Nota: Esta información es importante.",
    style={
        "font-size": "12px",
        "color": "#7f8c8d",
        "font-style": "italic"
    }
)

Button

The Button component creates interactive buttons that can execute actions.

Button Syntax

from dars.components.basic.button import Button

boton = Button(
    text="Hacer clic",
    button_type="button",  # "button", "submit", "reset"
    disabled=False,
    on_click=dScript("""
        function handleClick() {
            alert('Button clicked!');
        }
    """)
    style={
        "background-color": "#3498db",
        "color": "white",
        "padding": "10px 20px",
        "border": "none",
        "border-radius": "4px"
    }
)

Button Properties

Property Type Description Values
text str Button text "Enviar"
button_type str Button type "button" , "submit" , "reset"
disabled bool Si está deshabilitado True , False
on_click dScript Click handler dScript("function() { ... }")
on_double_click dScript Double click handler dScript("function() { ... }")
on_mouse_enter dScript Mouse enter handler dScript("function() { ... }")
on_mouse_leave dScript Mouse leave handler dScript("function() { ... }")
on_key_up dScript Key up handler dScript("function(e) { ... }")
on_key_press dScript Key press handler onKey(KeyCode.ENTER, action)

Button Examples

# Primary Button
primary_button = Button(
    text="Acción Principal",
    style={
        "background-color": "#007bff",
        "color": "white",
        "padding": "12px 24px",
        "border": "none",
        "border-radius": "6px",
        "font-size": "16px",
        "font-weight": "500",
        "cursor": "pointer",
        "transition": "background-color 0.3s"
    }
)

# Secondary Button
secondary_button = Button(
    text="Cancelar",
    style={
        "background-color": "transparent",
        "color": "#6c757d",
        "padding": "12px 24px",
        "border": "1px solid #6c757d",
        "border-radius": "6px",
        "font-size": "16px",
        "cursor": "pointer"
    }
)

# Danger Button
delete_button = Button(
    text="Eliminar",
    style={
        "background-color": "#dc3545",
        "color": "white",
        "padding": "8px 16px",
        "border": "none",
        "border-radius": "4px",
        "font-size": "14px"
    }
)

# Disabled Button
disabled_button = Button(
    text="No disponible",
    disabled=True,
    style={
        "background-color": "#e9ecef",
        "color": "#6c757d",
        "padding": "10px 20px",
        "border": "none",
        "border-radius": "4px",
        "cursor": "not-allowed"
    }
)

Input

The Input component allows user data entry.

Input Syntax

from dars.components.basic.input import Input

entrada = Input(
    value="Valor inicial",
    placeholder="Escribe aquí...",
    input_type="text",  # "text", "password", "email", "number", etc.
    disabled=False,
    readonly=False,
    required=False,
    max_length=100,
    on_change=dScript("""
        function handleChange(event) {
            console.log('Input changed:', event.target.value);
        }
    """)
    style={
        "width": "300px",
        "padding": "10px",
        "border": "1px solid #ddd",
        "border-radius": "4px"
    }
)

Input Properties

Property Type Description Values
value str Initial value "text"
placeholder str Help text "Ingresa tu name"
input_type str Tipo de entrada "text" , "password" , "email" , "number"
disabled bool Si está deshabilitado True , False
readonly bool Solo lectura True , False
required bool Campo obligatorio True , False
on_change dScript Change handler dScript("function(e) { ... }")
on_key_up dScript Key up handler dScript("function(e) { ... }")
on_key_press dScript Key press handler onKey(KeyCode.ENTER, action)
max_length int Longitud máxima 50
min_length int Longitud mínima 3
pattern str Validation pattern "[0-9]+"

Input Types

# Basic text input
name = Input(
    placeholder="Ingresa tu name",
    input_type="text",
    required=True,
    style={
        "width": "100%",
        "padding": "12px",
        "border": "2px solid #e1e5e9",
        "border-radius": "8px",
        "font-size": "16px"
    }
)

# Email input
email = Input(
    placeholder="tu@email.com",
    input_type="email",
    required=True,
    style={
        "width": "100%",
        "padding": "12px",
        "border": "2px solid #e1e5e9",
        "border-radius": "8px"
    }
)

# Password input
password = Input(
    placeholder="Contraseña",
    input_type="password",
    required=True,
    min_length=8,
    style={
        "width": "100%",
        "padding": "12px",
        "border": "2px solid #e1e5e9",
        "border-radius": "8px"
    }
)

# Numeric input
edad = Input(
    placeholder="Edad",
    input_type="number",
    style={
        "width": "100px",
        "padding": "8px",
        "border": "1px solid #ccc",
        "border-radius": "4px",
        "text-align": "center"
    }
)

# Search input
busqueda = Input(
    placeholder="Buscar...",
    input_type="search",
    style={
        "width": "300px",
        "padding": "10px 15px",
        "border": "1px solid #ddd",
        "border-radius": "20px",
        "background-color": "#f8f9fa"
    }
)

Container

The Container component is a container that can hold other components. It supports multiple ways to add child components.

Container Syntax

from dars.components.basic.container import Container

# Method 1: Pass components as arguments
container = Container(
    Text("Hello"),
    Button("Click me"),
    style={
        "display": "flex",
        "flex-direction": "column",
        "padding": "20px",
        "background-color": "#f8f9fa"
    }
)

# Method 2: Use additional_children parameter
components = [Text("Hello"), Button("Click me")]
container = Container(
    additional_children=components,
    style={
        "display": "flex",
        "flex-direction": "column",
        "padding": "20px",
        "background-color": "#f8f9fa"
    }
)

# Method 3: Add children after creation
container = Container(style={
    "display": "flex",
    "flex-direction": "column",
    "padding": "20px",
    "background-color": "#f8f9fa"
})
container.add_child(Text("Hello"))
container.add_child(Button("Click me"))

Container Properties

Property Type Description
children tuple Components passed as positional arguments
additional_children list Optional list of additional components

Container Layouts

# Vertical layout (column)
columna = Container(
    style={
        "display": "flex",
        "flex-direction": "column",
        "gap": "15px",
        "padding": "20px"
    }
)

# Horizontal layout (row)
fila = Container(
    style={
        "display": "flex",
        "flex-direction": "row",
        "gap": "20px",
        "align-items": "center"
    }
)

# Layout centrado
centrado = Container(
    style={
        "display": "flex",
        "justify-content": "center",
        "align-items": "center",
        "min-height": "100vh",
        "background-color": "#f0f2f5"
    }
)

# Card/Tarjeta
tarjeta = Container(
    style={
        "background-color": "white",
        "border-radius": "12px",
        "padding": "24px",
        "box-shadow": "0 2px 10px rgba(0,0,0,0.1)",
        "max-width": "400px",
        "margin": "20px auto"
    }
)

# Sidebar
sidebar = Container(
    style={
        "width": "250px",
        "height": "100vh",
        "background-color": "#2c3e50",
        "padding": "20px",
        "position": "fixed",
        "left": "0",
        "top": "0"
    }
)

Section

The Section component is a container that can hold other components. It supports multiple ways to add child components. And also its like the Container component but instead of export a <div> to render it exports and <section> .

Section Syntax

from dars.all import *
# Method 1: Pass components as arguments
container = Section(
    Text("Hello"),
    Button("Click me"),
    style={
        "display": "flex",
        "flex-direction": "column",
        "padding": "20px",
        "background-color": "#f8f9fa"
    }
)

# Method 2: Use additional_children parameter
components = [Text("Hello"), Button("Click me")]
container = Section(
    additional_children=components,
    style={
        "display": "flex",
        "flex-direction": "column",
        "padding": "20px",
        "background-color": "#f8f9fa"
    }
)

# Method 3: Add children after creation
container = Section(style={
    "display": "flex",
    "flex-direction": "column",
    "padding": "20px",
    "background-color": "#f8f9fa"
})
container.add_child(Text("Hello"))
container.add_child(Button("Click me"))

Section Properties

Property Type Description
children tuple Components passed as positional arguments
additional_children list Optional list of additional components

Video

The Video component is an advanced wrapper over the HTML5 <video> element. It supports static usage and full reactivity via State + useDynamic .

from dars.all import *
from dars.hooks.value_helpers import V

media_state = State(
    "media",
    current_video="/media/intro.mp4",
    autoplay_video=False,
    muted_video=True,
)

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",
    class_name="rounded-[8px] shadow-[0_0_20px_rgba(0,0,0,0.4)] mb-[16px]",
    attrs={
        "controlsList": "nodownload",
    },
)

Video Properties

Property Type Description Example
src str / useDynamic Video source URL (relative or absolute) "/media/intro.mp4"
poster str Poster image URL "/media/poster.jpg"
width str Width attribute "720" , "100%"
height str Height attribute "480"
controls bool / useDynamic Show native controls True , useDynamic("media.show_controls")
autoplay bool / useDynamic Autoplay video when ready False , useDynamic("media.autoplay_video")
loop bool / useDynamic Loop playback useDynamic("media.loop_video")
muted bool / useDynamic Start muted useDynamic("media.muted_video")
preload str Preload hint ( auto , metadata , none ) "metadata"
plays_inline bool Hint for inline playback on mobile True
class_name str CSS class name "my-video"
style dict / str Inline styles { "max-width": "100%" }
attrs dict Extra raw attributes { "controlsList": "nodownload" }

Video Reactivity Notes

  • src , autoplay , muted , loop , controls and plays_inline support useDynamic .
  • When the associated State changes, the exporter generates bindings that:
    • Update the src attribute directly.
    • For booleans ( autoplay , muted , loop , controls ), add/remove the HTML attribute and sync the DOM property.
  • Defaults:
    • controls=True , plays_inline=True by default.
    • autoplay , loop , muted are off by default unless you pass True or a state value.

Video Example with Toggles

from dars.all import *
from dars.hooks.value_helpers import V

media_state = State(
    "media",
    current_video="/media/intro.mp4",
    autoplay_video=False,
    muted_video=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)
                ),
            ),
        )
    )

Audio

The Audio component wraps the HTML5 <audio> element and supports the same reactive model.

from dars.all import *
from dars.hooks.value_helpers import V

media_state = State(
    "media",
    current_audio="/media/theme1.mp3",
    loop_audio=True,
)

Audio(
    src=useDynamic("media.current_audio"),
    controls=True,
    autoplay=False,
    loop=useDynamic("media.loop_audio"),
    preload="auto",
    class_name="w-[100%] mb-[12px]",
    attrs={"controlsList": "nodownload"},
)

Audio Properties

Property Type Description Example
src str / useDynamic Audio source URL "/media/theme1.mp3"
controls bool / useDynamic Show native controls True
autoplay bool / useDynamic Autoplay audio useDynamic("media.autoplay_audio")
loop bool / useDynamic Loop playback useDynamic("media.loop_audio")
muted bool / useDynamic Start muted True
preload str Preload hint ( auto , metadata , none ) "auto"
class_name str CSS class "audio-player"
style dict / str Inline styles { "width": "100%" }
attrs dict Extra attributes { "controlsList": "nodownload" }

Audio Reactivity Notes

  • src , loop , autoplay , muted , controls support useDynamic .
  • Boolean props are wired to the same reactive mechanism que otros componentes built-in:
    • El runtime añade/quita los atributos HTML y sincroniza las propiedades JS ( el.loop , el.muted , etc.).
  • Es recomendable ubicar los ficheros dentro de media/ en el root del proyecto y referenciarlos como /media/... . El exporter copiará automáticamente esa carpeta al directorio de export.

Audio Example with Track Switch

@route("/audio-demo")
def audio_demo():
    return Page(
        Container(
            Text("Audio actual:"),
            Text(useDynamic("media.current_audio")),
            Audio(
                src=useDynamic("media.current_audio"),
                controls=True,
                loop=useDynamic("media.loop_audio"),
                preload="auto",
            ),
            Container(
                Button(
                    "Track 1",
                    on_click=media_state.current_audio.set("/media/theme1.mp3"),
                ),
                Button(
                    "Track 2",
                    on_click=media_state.current_audio.set("/media/theme2.mp3"),
                ),
                Button(
                    "Toggle Loop",
                    on_click=media_state.loop_audio.set(
                        (V("media.loop_audio").bool() == True).then(False, True)
                    ),
                ),
            ),
        )
    )

Markdown

The Markdown component allows you to render markdown content directly in your Dars applications, converting markdown syntax to beautiful HTML with proper styling.

Markdown Syntax

from dars.components.basic.markdown import Markdown

# From string content
markdown_component = Markdown(
    content="# Welcome\nThis is **markdown** content",
    id="my-markdown",
    class_name="custom-markdown",
    style={"padding": "20px"}
)

# From file
markdown_from_file = Markdown(
    file_path="README.md",
    id="documentation",
    dark_theme=True
)

Markdown Properties

Property Type Description Example
content str Markdown content as string "# Heading"
file_path str Path to a markdown file "docs/intro.md"
dark_theme bool Enable dark theme styling True
id str Component ID "markdown-content"
class_name str CSS class "markdown-body"
style dict CSS styles {"fontSize": "16px"}

Markdown Methods

Method Description Example
update_content(new_content=None, new_file_path=None) Update markdown content markdown_component.update_content(new_content="# New")
set_dark_theme(enabled=True) Enable/disable dark theme markdown_component.set_dark_theme(True)

Markdown Examples

# Simple markdown from string
simple_md = Markdown(
    content="# Hello\nThis is a **markdown** example",
    style={"maxWidth": "800px", "margin": "0 auto"}
)

# Load from file with dark theme
docs_md = Markdown(
    file_path="documentation.md",
    dark_theme=True,
    class_name="docs-content"
)

# Update content dynamically
simple_md.update_content(new_content="# Updated\nNew content here")

The Markdown renderer supports fenced code blocks and emits standard language-<lang> classes, e.g. language-python .

By default, the exporter auto-injects a client-side highlighter (highlight.js) once per page and highlights all pre code blocks. This is controlled by markdownHighlight in dars.config.json .

Config example:

{
  "markdownHighlight": true
}
  • When true (default), highlight.js CSS/JS + init are added automatically.
  • When false , no assets are injected; include your own highlighter if you want colored code.

Fenced code example:

```python
import time
def hello():
    print("hi")
```

Markdown Dependencies

The Markdown component requires the markdown2 library. Included with the framework.

Supported Markdown Features

  • Headers ( # , ## , ### )
  • Bold and italic text
  • Lists (ordered and unordered)
  • Links
  • Inline code and code blocks
  • Tables
  • Blockquotes
  • Images
  • Horizontal rules

Markdown Styling

The Markdown component includes comprehensive default styling for both light and dark themes:

# Light theme (default)
markdown_light = Markdown(content="# Light theme")

# Dark theme
markdown_dark = Markdown(
    content="# Dark theme", 
    dark_theme=True,
    style={"padding": "20px", "borderRadius": "8px"}
)

Markdown Best Practices

  1. Use file paths for large documentation content
  2. Enable dark theme for better readability in low-light environments
  3. Combine with layout components for responsive designs
  4. Use the update methods for dynamic content changes

Markdown Integration Example

from dars.core.app import App
from dars.components.basic.markdown import Markdown
from dars.components.basic.container import Container

app = App(title="Documentation Viewer")

# Load documentation from file
docs = Markdown(
    file_path="README.md",
    dark_theme=True,
    class_name="documentation",
    style={
        "maxWidth": "800px",
        "margin": "0 auto",
        "padding": "40px",
        "lineHeight": "1.6"
    }
)

app.set_root(Container(children=[docs]))

This component is perfect for creating documentation pages, blog posts, content management systems, and any application that needs to display formatted text content.

Image

The Image component displays images.

Image Syntax

from dars.components.basic.image import Image

image = Image(
    src="path/to/your/image.jpg",
    alt="Descripción de la image",
    width="300px",
    height="200px",
    class_name="responsive-img",
    style={
        "border-radius": "8px",
        "box-shadow": "0 4px 8px rgba(0,0,0,0.1)"
    }
)

Image Properties

Property Type Description Example
src str Image path "images/logo.png"
alt str Alternative text "Logo of the company"
width str Ancho de la image (CSS) "100%" , "200px"
height str Alto de la image (CSS) "auto" , "150px"

The Link component creates navigation links.

from dars.components.basic.link import Link

link = Link(
    text="Visitar Google",
    href="https://www.google.com",
    target="_blank", # Abre en una nueva pestaña
    class_name="external-link",
    style={
        "color": "#007bff",
        "text-decoration": "none",
        "font-weight": "bold"
    }
)
Property Type Description Values
text str Link text "Ir a la página"
href str URL of destination "/about" , "https://example.com"
target str Dónde abrir el link "_self" (misma pestaña), "_blank" (nueva pestaña)

Textarea

The Textarea component allows for multi-line text input.

Textarea Syntax

from dars.components.basic.textarea import Textarea

area_text = Textarea(
    value="Texto inicial",
    placeholder="Escribe tu mensaje aquí...",
    rows=5,
    cols=40,
    disabled=False,
    readonly=False,
    required=True,
    max_length=500,
    class_name="comment-box",
    style={
        "width": "100%",
        "padding": "10px",
        "border": "1px solid #ccc",
        "border-radius": "5px"
    }
)

Textarea Properties

Property Type Description Values
value str Initial value ""
placeholder str Help text "Escribe aquí..."
rows int Número de filas visibles 4
cols int Número de columnas visibles 50
disabled bool Si está deshabilitado True , False
readonly bool Solo lectura True , False
required bool Campo obligatorio True , False
max_length int Longitud máxima 500

FileUpload

The FileUpload component allows users to select files for uploading. It wraps a standard file input with a custom, styleable interface.

FileUpload Syntax

from dars.components.advanced.file_upload import FileUpload

upload = FileUpload(
    id="doc-upload",
    label="Choose a file...",
    accept=".pdf,.doc,.docx",
    multiple=False,
    disabled=False,
    required=True,
    on_change=log("File uploaded"),
    style="m-0"
)

FileUpload Properties

Property Type Description Values
label str Text displayed on the button "Upload"
accept str File types to accept ".jpg,.png"
multiple bool Allow multiple files True , False
disabled bool Disable input True , False
required bool Mark as required True , False
on_change Callable Change handler log(...)

Chart

The Chart component renders interactive charts using Plotly.js.

Chart Syntax

from dars.components.visualization.chart import Chart
import plotly.graph_objects as go

fig = go.Figure(data=[go.Bar(x=['A', 'B', 'C'], y=[10, 20, 15])])

chart = Chart(
    figure=fig,
    width=800,
    height=400,
    style={"margin": "20px auto"}
)

Chart Properties

Property Type Description
figure plotly.graph_objects.Figure Plotly figure object
width int/str Width in pixels or CSS value
height int/str Height in pixels or CSS value
config dict Plotly configuration options

DataTable

The DataTable component displays tabular data with optional Pandas DataFrame support.

DataTable Syntax

from dars.components.visualization.table import DataTable

# Using list of dicts (recommended)
data = [
    {'Name': 'Alice', 'Age': 30, 'City': 'New York'},
    {'Name': 'Bob', 'Age': 25, 'City': 'London'}
]
table = DataTable(data, theme='dark')

# With custom columns and formatters
data = [{'price': 99.99, 'qty': 5}, {'price': 149.99, 'qty': 3}]
table = DataTable(
    data,
    columns=[
        {'key': 'price', 'label': 'Price', 'formatter': lambda x: f'${x:.2f}'},
        {'key': 'qty', 'label': 'Quantity', 'align': 'center'}
    ],
    striped=True,
    hover=True
)

DataTable Properties

Property Type Description Default
data DataFrame/list Data source -
columns list Column definitions Auto-inferred
index bool Show DataFrame index False
header bool Show header row True
striped bool Alternating row colors True
hover bool Hover effects True
bordered bool Cell borders True
compact bool Compact spacing False
theme str/dict 'light', 'dark', or custom 'light'

ProgressBar

The ProgressBar component visually displays progress for a task, such as loading or completion percentage.

ProgressBar Syntax

from dars.components.basic.progressbar import ProgressBar

progress = ProgressBar(value=40, max_value=100)

ProgressBar Properties

Property Type Description
value int Current progress value
max_value int Maximum value (default: 100)

ProgressBar Example

progress = ProgressBar(value=75, max_value=100)

Tooltip

The Tooltip component displays a tooltip when hovering over a child component.

Tooltip Syntax

from dars.components.basic.tooltip import Tooltip
from dars.components.basic.button import Button

tooltip = Tooltip(
    text="More info",
    child=Button(text="Hover me")
)

Tooltip Properties

Property Type Description
text str Tooltip text
child Component Component to wrap
position str Tooltip position (e.g., "top")

Tooltip Example

tooltip = Tooltip(text="Help", child=Button(text="?"))

Accordion

The Accordion component creates a vertically stacked set of expandable/collapsible panels for organizing content.

Accordion Syntax

from dars.components.advanced.accordion import Accordion

accordion = Accordion(
    items=[
        {"title": "Section 1", "content": "Content for section 1"},
        {"title": "Section 2", "content": "Content for section 2"}
    ],
    allow_multiple=False
)

Accordion Properties

Property Type Description
items list List of dicts with title and content
allow_multiple bool Allow multiple sections open at once

Accordion Example

accordion = Accordion(
    items=[
        {"title": "FAQ 1", "content": "Answer 1"},
        {"title": "FAQ 2", "content": "Answer 2"}
    ]
)

Tabs

The Tabs component allows navigation between different views or content panels.

New in 1.0.5: The exporter now recursively detects Tabs at any nesting level (including inside containers, panels, or multipage apps) for minimum_logic and JS injection. You can safely nest Tabs in any structure and the export will work as expected.

Tabs Syntax

from dars.components.advanced.tabs import Tabs

tabs = Tabs(
    tabs=[
        {"label": "Tab 1", "content": "Content 1"},
        {"label": "Tab 2", "content": "Content 2"}
    ],
    default_index=0
)

Tabs Properties

Property Type Description
tabs list List of dicts with label and content
default_index int Index of the initially selected tab

Tabs Example

tabs = Tabs(
    tabs=[
        {"label": "Overview", "content": "Main content"},
        {"label": "Details", "content": "Detailed info"}
    ],
    default_index=0
)

Table

The Table component displays tabular data with rows and columns.

Table Syntax

from dars.components.advanced.table import Table

table = Table(
    columns=["Name", "Age", "Country"],
    data=[
        ["Alice", 30, "USA"],
        ["Bob", 25, "UK"]
    ]
)

Table Properties

Property Type Description
columns list List of column headers
data list List of rows (each a list/tuple)

Table Example

table = Table(
    columns=["Product", "Price"],
    data=[
        ["Book", "$10"],
        ["Pen", "$2"]
    ]
)

Layout Components

GridLayout

The GridLayout component provides a responsive grid-based layout with customizable rows, columns, gaps, and anchor points for precise positioning of children.

GridLayout Syntax

from dars.components.layout.grid import GridLayout
from dars.components.basic.text import Text

grid = GridLayout(
    rows=2,
    cols=2,
    gap="24px",
    children=[
        Text("Top Left"),
        Text("Top Right"),
        Text("Bottom Left"),
        Text("Bottom Right")
    ]
)

GridLayout Properties

Property Type Description
rows int Number of grid rows
cols int Number of grid columns
gap str Gap between grid cells (e.g., "16px")
children list List of child components
anchors dict Optional anchor points for child placement

GridLayout Example

grid = GridLayout(
    rows=3,
    cols=2,
    gap="16px",
    children=[Text(f"Cell {i}") for i in range(6)]
)

FlexLayout

The FlexLayout component provides a responsive flexbox layout, supporting direction, wrap, alignment, and gap between children. Useful for row/column layouts.

FlexLayout Syntax

from dars.components.layout.flex import FlexLayout
from dars.components.basic.button import Button

flex = FlexLayout(
    direction="row",
    justify="space-between",
    align="center",
    gap="12px",
    children=[Button("A"), Button("B"), Button("C")]
)

FlexLayout Properties

Property Type Description
direction str Flex direction: "row" or "column"
wrap str Flex wrap: "wrap" or "nowrap"
justify str Justify content: e.g., "flex-start", "center"
align str Align items: e.g., "stretch", "center"
gap str Gap between children (e.g., "16px")
children list List of child components
anchors dict Optional anchor points for child placement

FlexLayout Example

flex = FlexLayout(
    direction="column",
    gap="24px",
    children=[Button("Save"), Button("Cancel")]
)

LayoutBase

The LayoutBase component is the base class for all layout components. It allows adding children and anchor/positioning info. You typically use FlexLayout or GridLayout directly.

LayoutBase Syntax

from dars.components.layout.grid import LayoutBase
from dars.components.basic.text import Text

layout = LayoutBase(
    children=[Text("Item 1"), Text("Item 2")],
    anchors={}
)

LayoutBase Properties

Property Type Description
children list List of child components
anchors dict Anchor/positioning information

AnchorPoint

The AnchorPoint class represents an anchor or alignment point for a child in a layout (e.g., top, left, right, bottom, center, percent, or px).

AnchorPoint Syntax

from dars.components.layout.anchor import AnchorPoint

anchor = AnchorPoint(x="left", y="top", name="top-left")

AnchorPoint Properties

Property Type Description
x str Horizontal alignment (e.g., "left", "center")
y str Vertical alignment (e.g., "top", "center")
name str Optional semantic name for the anchor

AnchorPoint Example

anchor = AnchorPoint(x="50%", y="50%", name="center")

Card

The Card component is a styled container to group related content, such as a title and other components.

Card Syntax

from dars.components.basic.card import Card
from dars.components.basic.text import Text
from dars.components.basic.button import Button

my_card = Card(
    title="Título de la Tarjeta",
    children=[
        Text("Este es el contenido de la tarjeta."),
        Button("Ver más")
    ],
    class_name="product-card",
    style={
        "background-color": "#ffffff",
        "border": "1px solid #e0e0e0",
        "border-radius": "10px",
        "padding": "20px",
        "box-shadow": "0 4px 8px rgba(0,0,0,0.05)"
    }
)

Card Properties

Property Type Description
title str Card title
children list List of child components

Card Example

my_card = Card(
    title="Título de la Tarjeta",
    children=[
        Text("Este es el contenido de la tarjeta."),
        Button("Ver más")
    ],
    class_name="product-card",
    style={
        "background-color": "#ffffff",
        "border": "1px solid #e0e0e0",
        "border-radius": "10px",
        "padding": "20px",
        "box-shadow": "0 4px 8px rgba(0,0,0,0.05)"
    }
)

The Modal component creates an overlay window that appears on top of the main page content.

New in 1.0.5: Modal is now exported as hidden by default ( hidden attribute and dars-modal-hidden class) if is_open=False , preventing any visual flicker on page load, even if CSS/JS loads slowly.

from dars.components.advanced.modal import Modal
from dars.components.basic.text import Text
from dars.components.basic.button import Button

my_modal = Modal(
    title="Welcome to the Modal",
    is_open=False, # Now hidden from the very first render
    children=[
        Text("This is your modal content."),
        Button("Close")
    ],
    class_name="welcome-modal",
    style={
        "background-color": "rgba(0, 0, 0, 0.7)" # Overlay style
    }
)
Property Type Description
title str Modal title
is_open bool Controls modal visibility ( True to show, False to hide). If False , modal is hidden from exported HTML.
children list List of child components
my_modal = Modal(
    title="Welcome to the Modal",
    is_open=False,  # Hidden from the very first render
    children=[
        Text("This is your modal content."),
        Button("Close")
    ],
    class_name="welcome-modal",
    style={
        "background-color": "rgba(0, 0, 0, 0.7)"
    }
)

Note: The exporter now recursively detects advanced components (Tabs, Accordion, Modal, Card) at any nesting level, including inside multipage apps, and applies minimum_logic robustly.


The Navbar component creates a navigation bar, commonly used at the top of pages.

from dars.components.advanced.navbar import Navbar
from dars.components.basic.link import Link

my_navbar = Navbar(
    brand="Mi App",
    children=[
        Link("Inicio", "/"),
        Link("Acerca de", "/about"),
        Link("Contacto", "/contact")
    ],
    class_name="main-nav",
    style={
        "background-color": "#333",
        "color": "white",
        "padding": "15px 20px"
    }
)
Property Type Description
brand str Texto o componente para la marca/logo de la navegación
children list List of child components (navigation items, usually Link s)

Additional Components

Checkbox

The Checkbox component allows users to select options.

Checkbox Syntax

from dars.components.basic.checkbox import Checkbox

mi_checkbox = Checkbox(
    label="Acepto términos",
    checked=True,
    style={
        "margin": "10px"
    }
)

Checkbox Properties

Property Type Description Values
label str Texto de la etiqueta "Acepto términos"
checked bool Estado de selección True , False

RadioButton

The RadioButton component allows users to select one option from a group of options.

RadioButton Syntax

from dars.components.basic.radio_button import RadioButton

mi_radio_button = RadioButton(
    label="Opción A",
    name="grupo1",
    checked=False,
    style={
        "margin": "10px"
    }
)

RadioButton Properties

Property Type Description Values
label str Texto de la etiqueta "Opción A"
name str Nombre del grupo de radio buttons "grupo1"
checked bool Estado de selección True , False

Select

The Select component allows users to select one option from a group of options.

Select Syntax

from dars.components.basic.select import Select

mi_select = Select(
    options=["Uno", "Dos", "Tres"],
    value="Dos",
    style={
        "width": "200px",
        "padding": "10px",
        "border": "1px solid #ccc"
    }
)

Select Properties

Property Type Description Values
options list List of options ["Uno", "Dos", "Tres"]
value str Selected value "Dos"

Slider

The Slider component allows users to select a value within a range.

Slider Syntax

from dars.components.basic.slider import Slider

mi_slider = Slider(
    min_value=0,
    max_value=100,
    value=50,
    show_value=True,
    style={
        "width": "200px",
        "padding": "10px"
    }
)

Slider Properties

Property Type Description Values
min_value int Minimum value 0
max_value int Maximum value 100
value int Valor selectado 50
show_value bool Mostrar el valor selectado True , False

DatePicker

The DatePicker component allows users to select a date.

DatePicker Syntax

from dars.components.basic.date_picker import DatePicker

mi_date_picker = DatePicker(
    value="2025-08-06",
    style={
        "width": "200px",
        "padding": "10px",
        "border": "1px solid #ccc"
    }
)

DatePickerProperties

Property Type Description Values
value str Selected date "2025-08-06"

Styling System

Supported Style Properties

Dars supports most standard CSS properties:

Dimensions

  • width , height
  • min-width , min-height
  • max-width , max-height

Spacing

  • margin , margin-top , margin-right , margin-bottom , margin-left
  • padding , padding-top , padding-right , padding-bottom , padding-left

Colors

  • background-color
  • color
  • border-color

Typography

  • font-size , font-family , font-weight , font-style
  • text-align , text-decoration , line-height

Borders

  • border , border-width , border-style , border-radius

Layout

  • display , position
  • top , right , bottom , left , z-index

Flexbox

  • flex-direction , flex-wrap
  • justify-content , align-items , align-content
  • flex , flex-grow , flex-shrink , flex-basis

Grid

  • grid-template-columns , grid-template-rows
  • grid-gap , grid-column , grid-row

Effects

  • opacity , box-shadow , transform , transition

Advanced Style Examples

# Gradiente de fondo
gradiente = Container(
    style={
        "background": "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
        "min-height": "100vh",
        "display": "flex",
        "align-items": "center",
        "justify-content": "center"
    }
)

# Animación de hover (para web)
boton_animado = Button(
    text="Hover me",
    style={
        "background-color": "#3498db",
        "color": "white",
        "padding": "15px 30px",
        "border": "none",
        "border-radius": "8px",
        "transition": "all 0.3s ease",
        "transform": "translateY(0)",
        "box-shadow": "0 4px 15px rgba(52, 152, 219, 0.3)"
    }
)

# Layout de grid
grid_container = Container(
    style={
        "display": "grid",
        "grid-template-columns": "repeat(auto-fit, minmax(250px, 1fr))",
        "grid-gap": "20px",
        "padding": "20px"
    }
)

# Responsive design
responsive_container = Container(
    style={
        "width": "100%",
        "max-width": "1200px",
        "margin": "0 auto",
        "padding": "0 20px"
    }
)

Best Practices Styling

Component Organization

def create_header():
    return Container(
        children=[
            Text("My Application", style={"font-size": "24px", "font-weight": "bold"}),
            Text("Descriptive subtitle", style={"color": "#666"})
        ],
        style={
            "padding": "20px",
            "background-color": "#f8f9fa",
            "border-bottom": "1px solid #dee2e6"
        }
    )

def create_form():
    return Container(
        children=[
            Text("Contact Form", style={"font-size": "20px", "margin-bottom": "20px"}),
            Input(placeholder="Name", style={"margin-bottom": "10px"}),
            Input(placeholder="Email", input_type="email", style={"margin-bottom": "10px"}),
            Button("Send", style={"background-color": "#007bff", "color": "white"})
        ],
        style={
            "max-width": "400px",
            "margin": "20px auto",
            "padding": "20px"
        }
    )

Style Reuse

# Define common styles
BASE_BUTTON_STYLES = {
    "padding": "10px 20px",
    "border": "none",
    "border-radius": "4px",
    "font-size": "14px",
    "cursor": "pointer"
}

PRIMARY_BUTTON_STYLES = {
    **BASE_BUTTON_STYLES,
    "background-color": "#007bff",
    "color": "white"
}

SECONDARY_BUTTON_STYLES = {
    **BASE_BUTTON_STYLES,
    "background-color": "#6c757d",
    "color": "white"
}

# Use in components
cancel_button = Button("Cancelar", style=SECONDARY_BUTTON_STYLES)
save_button = Button("Guardar", style=PRIMARY_BUTTON_STYLES)

Components provide a solid foundation for creating modern and responsive user interfaces that can be exported to multiple platforms while maintaining consistency and functionality.

Styling System in Dars

Dars Framework introduces a powerful, Python-native utility class system inspired by Tailwind CSS. This system allows you to style your components using concise string utilities directly in your Python code, without needing Node.js, PostCSS, or any external build tools.

Tip: The official Dars Framework VS Code extension provides Tailwind-like utility style completions while editing style="..." strings in Python.

  • VS Code Marketplace: https://marketplace.visualstudio.com/items?itemName=ZtaMDev.dars-framework
  • Open VSX: https://open-vsx.org/extension/ztamdev/dars-framework

Overview

Instead of writing raw CSS dictionaries or separate CSS files, you can now use the style , hover_style , and active_style arguments with utility strings. These strings are parsed at runtime (and export time) into standard CSS dictionaries.

Example

from dars.all import *

def MyComponent():
    return Container(
        Text("Hello, Dars!"),
        style="bg-blue-500 p-4 rounded-lg shadow-md",
        hover_style="bg-blue-600 scale-105",
        active_style="scale-95"
    )

Supported Utilities

The system supports a comprehensive range of utilities covering layout, spacing, typography, colors, borders, effects, transforms, and more.

Arbitrary Properties (Tailwind-like)

You can set any CSS property using the prop-[value] syntax.

  • Property names use standard CSS (with - ). If you prefer, you can also write underscores ( _ ) and Dars will convert them to dashes.
  • Inside [value] , underscores ( _ ) are converted to spaces. This makes it easy to write complex CSS values inside a single class token.

Examples:

style="background-image-[linear-gradient(90deg,_rgba(0,0,0,.35),_#00ffcc)]"
style="background-color-[rgba(10,20,30,.6)]"
style="padding-[calc(1rem_+_2vw)]"
style="color-[var(--brand-color)]"
style="--brand-color-[#00ffcc]"

Composable properties

Some properties are composable, meaning multiple utilities will be appended (instead of overwriting the previous value):

  • filter
  • backdrop-filter
  • transform
style="filter-[blur(6px)] filter-[brightness(120%)]"

This produces:

filter: blur(6px) brightness(120%);

Layout & Display

  • Display : flex , inline-flex , grid , inline-grid , block , inline-block , inline , table , table-row , table-cell , hidden , contents , flow-root
  • Flex Direction : flex-row , flex-row-reverse , flex-col , flex-col-reverse
  • Flex Wrap : flex-wrap , flex-wrap-reverse , flex-nowrap
  • Justify Content : justify-start , justify-end , justify-center , justify-between , justify-around , justify-evenly
  • Justify Items : justify-items-start , justify-items-end , justify-items-center , justify-items-stretch
  • Align Items : items-start , items-end , items-center , items-baseline , items-stretch
  • Align Content : content-start , content-end , content-center , content-between , content-around , content-evenly
  • Align Self : self-auto , self-start , self-end , self-center , self-stretch , self-baseline
  • Flex Grow/Shrink : grow , grow-0 , shrink , shrink-0
  • Flex : flex-1 , flex-auto , flex-initial , flex-none
  • Order : order-{n}
  • Basis : basis-{value}

Grid

  • Grid Template Columns : grid-cols-{n} (e.g., grid-cols-3 , grid-cols-12 )
  • Grid Template Rows : grid-rows-{n}
  • Grid Column Span : col-span-{n} , col-start-{n} , col-end-{n}
  • Grid Row Span : row-span-{n} , row-start-{n} , row-end-{n}
  • Grid Auto Flow : grid-flow-row , grid-flow-col , grid-flow-dense , grid-flow-row-dense , grid-flow-col-dense
  • Grid Auto Columns : auto-cols-auto , auto-cols-min , auto-cols-max , auto-cols-fr
  • Grid Auto Rows : auto-rows-auto , auto-rows-min , auto-rows-max , auto-rows-fr
  • Gap : gap-{n} , gap-x-{n} , gap-y-{n}

Spacing (Padding & Margin)

  • Padding : p-{n} (all sides), px-{n} (horizontal), py-{n} (vertical), pt-{n} (top), pr-{n} (right), pb-{n} (bottom), pl-{n} (left), ps-{n} (inline-start), pe-{n} (inline-end)
  • Margin : m-{n} , mx-{n} , my-{n} , mt-{n} , mr-{n} , mb-{n} , ml-{n} , ms-{n} , me-{n}
  • Margin Auto : mx-auto , my-auto
  • Values :
    • Numbers correspond to 0.25rem units (e.g., 4 = 1rem , 8 = 2rem )
    • Arbitrary values: p-[20px] , m-[5%]

Sizing

  • Width : w-{n} , w-full , w-screen , w-auto , w-min , w-max , w-fit , w-1/2 , w-1/3 , w-[300px]
  • Height : h-{n} , h-full , h-screen , h-auto , h-min , h-max , h-fit , h-[50vh]
  • Min Width : min-w-{n} , min-w-full , min-w-min , min-w-max , min-w-fit
  • Min Height : min-h-{n} , min-h-full , min-h-screen , min-h-min , min-h-max , min-h-fit
  • Max Width : max-w-{size} (xs, sm, md, lg, xl, 2xl-7xl, full, min, max, fit, prose, screen-{size})
  • Max Height : max-h-{n} , max-h-full , max-h-screen , max-h-min , max-h-max , max-h-fit , max-h-none
  • Size : size-{n} (sets both width and height)

Typography

  • Font Size : text-xs , text-sm , text-base , text-lg , text-xl , text-2xl , text-3xl , text-4xl , text-5xl , text-6xl , text-7xl , text-8xl , text-9xl
  • Font Size (Direct) : fs-[32px] , fs-[2rem] Direct font-size specification
  • Font Family : ffam-sans , ffam-serif , ffam-mono , ffam-[Open_Sans] , ffam-[Times+New+Roman]
  • Font Weight : font-thin , font-extralight , font-light , font-normal , font-medium , font-semibold , font-bold , font-extrabold , font-black
  • Font Style : italic , not-italic
  • Text Align : text-left , text-center , text-right , text-justify , text-start , text-end
  • Text Color : text-{color}-{shade} , text-[#123456]
  • Text Decoration : underline , overline , line-through , no-underline
  • Text Transform : uppercase , lowercase , capitalize , normal-case
  • Text Overflow : truncate , text-ellipsis , text-clip
  • Vertical Align : align-baseline , align-top , align-middle , align-bottom , align-text-top , align-text-bottom , align-sub , align-super
  • Whitespace : whitespace-normal , whitespace-nowrap , whitespace-pre , whitespace-pre-line , whitespace-pre-wrap , whitespace-break-spaces
  • Word Break : break-normal , break-words , break-all , break-keep
  • Line Height : leading-none , leading-tight , leading-snug , leading-normal , leading-relaxed , leading-loose , leading-{n}
  • Letter Spacing : tracking-tighter , tracking-tight , tracking-normal , tracking-wide , tracking-wider , tracking-widest
  • Text Indent : indent-{n}

Colors

Complete Palette (50-950 shades for each):

  • Neutrals : slate , gray , zinc , neutral , stone
  • Reds : red , rose
  • Oranges : orange , amber
  • Yellows : yellow , lime
  • Greens : green , emerald , teal
  • Blues : cyan , sky , blue , indigo
  • Purples : violet , purple , fuchsia
  • Pinks : pink
  • Special : black , white , transparent , current

Usage Examples:

style="bg-cyan-500 text-white"        # Cyan background
style="bg-emerald-600 text-slate-50"  # Emerald background with light slate text
style="bg-fuchsia-500 text-rose-100"  # Fuchsia background with light rose text
style="bg-amber-400 text-zinc-900"    # Amber background with dark zinc text

Backgrounds

  • Background Color : bg-{color}-{shade} , bg-[#f0f0f0]
  • Background Images & Gradients :
    • bg-[linear-gradient(...)] (automatically maps to background-image )
    • bg-[radial-gradient(...)]
    • bg-[url(...)]
    • bgimg-[...] (direct background-image utility)
  • Background Position : bg-bottom , bg-center , bg-left , bg-left-bottom , bg-left-top , bg-right , bg-right-bottom , bg-right-top , bg-top
  • Background Repeat : bg-repeat , bg-no-repeat , bg-repeat-x , bg-repeat-y , bg-repeat-round , bg-repeat-space
  • Background Size : bg-auto , bg-cover , bg-contain
  • Background Attachment : bg-fixed , bg-local , bg-scroll
  • Background Clip : bg-clip-border , bg-clip-padding , bg-clip-content , bg-clip-text
  • Background Origin : bg-origin-border , bg-origin-padding , bg-origin-content
  • Background Blend Mode : bg-blend-normal , bg-blend-multiply , bg-blend-screen , bg-blend-overlay , bg-blend-darken , bg-blend-lighten , bg-blend-color-dodge , bg-blend-color-burn , bg-blend-hard-light , bg-blend-soft-light , bg-blend-difference , bg-blend-exclusion , bg-blend-hue , bg-blend-saturation , bg-blend-color , bg-blend-luminosity

Borders & Radius

  • Border Width : border , border-0 , border-2 , border-4 , border-8
  • Border Width (Sides) : border-t-{n} , border-r-{n} , border-b-{n} , border-l-{n} , border-x-{n} , border-y-{n}
  • Border Color : border-{color}-{shade} , border-[#123456]
  • Border Color (Sides) : border-t-{color} , border-r-{color} , border-b-{color} , border-l-{color} , border-x-{color} , border-y-{color}
  • Border Style : border-solid , border-dashed , border-dotted , border-double , border-hidden , border-none
  • Border Radius : rounded , rounded-none , rounded-sm , rounded-md , rounded-lg , rounded-xl , rounded-2xl , rounded-3xl , rounded-full , rounded-[10px]
  • Border Radius (Corners) : rounded-t-{size} , rounded-r-{size} , rounded-b-{size} , rounded-l-{size} , rounded-tl-{size} , rounded-tr-{size} , rounded-br-{size} , rounded-bl-{size}

Effects

  • Box Shadow : shadow-sm , shadow , shadow-md , shadow-lg , shadow-xl , shadow-2xl , shadow-inner , shadow-none
  • Opacity : opacity-0 , opacity-25 , opacity-50 , opacity-75 , opacity-100
  • Mix Blend Mode : mix-blend-normal , mix-blend-multiply , mix-blend-screen , mix-blend-overlay , mix-blend-darken , mix-blend-lighten , mix-blend-color-dodge , mix-blend-color-burn , mix-blend-hard-light , mix-blend-soft-light , mix-blend-difference , mix-blend-exclusion , mix-blend-hue , mix-blend-saturation , mix-blend-color , mix-blend-luminosity

Filters

  • Blur : blur-none , blur-sm , blur , blur-md , blur-lg , blur-xl , blur-2xl , blur-3xl
  • Brightness : brightness-{n} (0-200)
  • Contrast : contrast-{n} (0-200)
  • Grayscale : grayscale-{n} (0-100)
  • Hue Rotate : hue-rotate-{n} (degrees)
  • Invert : invert-{n} (0-100)
  • Saturate : saturate-{n} (0-200)
  • Sepia : sepia-{n} (0-100)
  • Drop Shadow : drop-shadow-sm , drop-shadow , drop-shadow-md , drop-shadow-lg , drop-shadow-xl , drop-shadow-2xl , drop-shadow-none

Backdrop Filters

  • Backdrop Blur : backdrop-blur-{size}
  • Backdrop Brightness : backdrop-brightness-{n}
  • Backdrop Contrast : backdrop-contrast-{n}
  • Backdrop Grayscale : backdrop-grayscale-{n}
  • Backdrop Hue Rotate : backdrop-hue-rotate-{n}
  • Backdrop Invert : backdrop-invert-{n}
  • Backdrop Opacity : backdrop-opacity-{n}
  • Backdrop Saturate : backdrop-saturate-{n}
  • Backdrop Sepia : backdrop-sepia-{n}

Transforms

  • Scale : scale-{n} , scale-x-{n} , scale-y-{n} (e.g., scale-110 = 110%)
  • Rotate : rotate-{n} (degrees)
  • Translate : translate-x-{n} , translate-y-{n}
  • Skew : skew-x-{n} , skew-y-{n} (degrees)
  • Transform Origin : origin-center , origin-top , origin-top-right , origin-right , origin-bottom-right , origin-bottom , origin-bottom-left , origin-left , origin-top-left

Transitions

  • Transition Property : transition-none , transition-all , transition-colors , transition-opacity , transition-shadow , transition-transform
  • Transition Duration : duration-{ms} (e.g., duration-300 , duration-500 )
  • Transition Delay : delay-{ms}
  • Transition Timing : ease-linear , ease-in , ease-out , ease-in-out

Positioning

  • Position : static , fixed , absolute , relative , sticky
  • Top/Right/Bottom/Left : top-{n} , right-{n} , bottom-{n} , left-{n}
  • Inset : inset-{n} , inset-x-{n} , inset-y-{n}
  • Z-Index : z-{n} (e.g., z-10 , z-50 )

Overflow

  • Overflow : overflow-auto , overflow-hidden , overflow-clip , overflow-visible , overflow-scroll
  • Overflow X : overflow-x-auto , overflow-x-hidden , overflow-x-clip , overflow-x-visible , overflow-x-scroll
  • Overflow Y : overflow-y-auto , overflow-y-hidden , overflow-y-clip , overflow-y-visible , overflow-y-scroll

Visibility

  • Visibility : visible , invisible , collapse

Cursor

  • Cursor Types : cursor-auto , cursor-default , cursor-pointer , cursor-wait , cursor-text , cursor-move , cursor-help , cursor-not-allowed , cursor-none , cursor-context-menu , cursor-progress , cursor-cell , cursor-crosshair , cursor-vertical-text , cursor-alias , cursor-copy , cursor-no-drop , cursor-grab , cursor-grabbing , cursor-all-scroll , cursor-col-resize , cursor-row-resize , cursor-n-resize , cursor-e-resize , cursor-s-resize , cursor-w-resize , cursor-ne-resize , cursor-nw-resize , cursor-se-resize , cursor-sw-resize , cursor-ew-resize , cursor-ns-resize , cursor-nesw-resize , cursor-nwse-resize , cursor-zoom-in , cursor-zoom-out

Pointer Events & User Select

  • Pointer Events : pointer-events-none , pointer-events-auto
  • User Select : select-none , select-text , select-all , select-auto

Object Fit & Position

  • Object Fit : object-contain , object-cover , object-fill , object-none , object-scale-down
  • Object Position : object-bottom , object-center , object-left , object-left-bottom , object-left-top , object-right , object-right-bottom , object-right-top , object-top

Aspect Ratio

  • Aspect Ratio : aspect-auto , aspect-square , aspect-video , aspect-{w}-{h} (e.g., aspect-16-9 )

Float & Clear

  • Float : float-right , float-left , float-none
  • Clear : clear-left , clear-right , clear-both , clear-none

Box Sizing

  • Box Sizing : box-border , box-content

Isolation

  • Isolation : isolate , isolation-auto

List Styles

  • List Style Type : list-none , list-disc , list-decimal
  • List Style Position : list-inside , list-outside

Appearance

  • Appearance : appearance-none , appearance-auto

Resize

  • Resize : resize-none , resize-y , resize-x , resize

Scroll Behavior

  • Scroll Behavior : scroll-auto , scroll-smooth
  • Scroll Snap Align : snap-start , snap-end , snap-center , snap-align-none
  • Scroll Snap Stop : snap-normal , snap-always
  • Scroll Snap Type : snap-none , snap-x , snap-y , snap-both , snap-mandatory , snap-proximity

Scroll Margin & Padding

  • Scroll Margin : scroll-m-{n} , scroll-mx-{n} , scroll-my-{n} , scroll-mt-{n} , scroll-mr-{n} , scroll-mb-{n} , scroll-ml-{n}
  • Scroll Padding : scroll-p-{n} , scroll-px-{n} , scroll-py-{n} , scroll-pt-{n} , scroll-pr-{n} , scroll-pb-{n} , scroll-pl-{n}

Touch Action

  • Touch Action : touch-auto , touch-none , touch-pan-x , touch-pan-left , touch-pan-right , touch-pan-y , touch-pan-up , touch-pan-down , touch-pinch-zoom , touch-manipulation

Will Change

  • Will Change : will-change-auto , will-change-scroll , will-change-contents , will-change-transform

Columns

  • Columns : columns-{n}

Break After/Before/Inside

  • Break After : break-after-{value}
  • Break Before : break-before-{value}
  • Break Inside : break-inside-{value}

Arbitrary Values

For values not covered by the standard scale, use square brackets [] :

Container(
    style="w-[350px] bg-[#1a2b3c] z-[100] top-[50px] fs-[24px]"
)

Arbitrary value features: - Use underscores for spaces: bg-[url('image.jpg')] bg-[url('image.jpg')] - Works with any property: p-[20px] , m-[5%] , w-[calc(100%-50px)]

State Variants

You can define styles for specific states using hover_style and active_style arguments.

Button(
    "Click Me",
    style="bg-blue-500 text-white px-4 py-2 rounded transition-all duration-300",
    hover_style="bg-blue-600 shadow-lg scale-105",
    active_style="bg-blue-700 scale-95"
)

Complete Example Styling

from dars.all import *

app = App("Styling Demo")

@route("/")
def index():
    return Page(
        Container(
            # Header with gradient text
            Text(
                "Welcome to Dars",
                style="fs-[48px] font-black text-center mb-8"
            ),

            # Card with new colors
            Container(
                Text("Cyan Card", style="text-xl font-bold mb-2"),
                Text("Using the new cyan color palette", style="text-cyan-100"),
                style="bg-cyan-600 p-6 rounded-xl shadow-xl mb-4"
            ),

            Container(
                Text("Emerald Card", style="text-xl font-bold mb-2"),
                Text("With emerald green background", style="text-emerald-100"),
                style="bg-emerald-600 p-6 rounded-xl shadow-xl mb-4"
            ),

            # Interactive button
            Button(
                "Hover Me",
                style="bg-fuchsia-500 text-white px-6 py-3 rounded-lg transition-all duration-300",
                hover_style="bg-fuchsia-600 scale-110 shadow-2xl",
                active_style="scale-95"
            ),

            style="max-w-4xl mx-auto p-8"
        ),
        style="min-h-screen bg-gradient-to-br from-slate-50 to-zinc-100"
    )

app.add_page("index", index())

if __name__ == "__main__":
    app.rTimeCompile()

Performance

The parsing happens in Python before the HTML/CSS is generated. This means:

  1. Zero Runtime Overhead : The browser receives standard CSS
  2. No JavaScript Dependency : No need to load a large utility CSS library or run JS parsers in the browser
  3. Optimized Output : Only the styles you use are generated (as inline styles or extracted CSS)
  4. Python-Native : No Node.js, PostCSS, or build tools required

Best Practices

  1. Use semantic spacing : Stick to the 0.25rem scale (4, 8, 12, 16, etc.) for consistency
  2. Leverage the color palette : Use the predefined color shades for a cohesive design
  3. Combine with transitions : Add transition-all duration-300 for smooth hover effects
  4. Use arbitrary values sparingly : Prefer standard utilities when possible
  5. Keep it readable : Break long utility strings into multiple lines if needed
style="""
    bg-gradient-to-r from-purple-600 to-pink-600
    text-white px-8 py-4 rounded-xl shadow-2xl
    transition-all duration-300
"""

For animations, see the Animation System documentation.

Custom Utilities

You can define your own utility classes in dars.config.json under the utility_styles key. This allows you to create reusable style combinations, use raw CSS properties, and even compose other custom utilities.

Configuration ( dars.config.json ):

{
  "utility_styles": {
    "btn-primary": [
      "bg-blue-600", 
      "text-white", 
      "p-3", 
      "rounded-lg", 
      "hover:bg-blue-700", 
      "transition-all"
    ],
    "card-fancy": [
      "bg-white", 
      "p-8", 
      "rounded-xl", 
      "shadow-lg", 
      "border: 1px solid #e5e7eb"  // Raw CSS property
    ],
    "text-gradient": [
      "font-bold",
      "text-4xl",
      "background: linear-gradient(to right, #4f46e5, #ec4899)",
      "-webkit-background-clip: text",
      "-webkit-text-fill-color: transparent",
      "display: inline-block"
    ]
  }
}

Usage in Python:

Button(text="Click Me", style="btn-primary")
Container(style="card-fancy text-gradient")

Features: - Composition : Combine multiple existing utilities into one class. - Raw CSS : Use standard CSS syntax (e.g., border: 1px solid red ) directly in the list.

Custom Components in Dars Framework

Dars offers two ways to create custom components: Function Components (Recommended) and Class Components (Legacy).

Function Components are the modern way to create reusable UI elements. They use simple functions with f-string templates and automatically handle framework features like IDs, styling, and events.

Basic Syntax

Use the @FunctionComponent decorator. You can access framework properties ( id , class_name , style , children ) using the Props helper object or by declaring them as arguments.

Option 1: Using Props Object (Cleanest)

from dars.all import *

@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 2: Explicit Arguments

@FunctionComponent
def UserCard(name, email, id, class_name, style, children, **props):
    return f"""
    <div {id} {class_name} {style}>
        <h3>{name}</h3>
        <p>{email}</p>
        <div class="card-body">
            {children}
        </div>
    </div>
    """

Key Features

  1. Automatic Property Injection : The framework automatically injects the correct HTML attributes for {id} , {class_name} , and {style} .
  2. State V2 Compatible : Function components work seamlessly with State() and reactive updates.
  3. Event Handling : Events like on_click are handled automatically by the framework (passed via **props ).
  4. Children Support : Use {Props.children} or {children} to render nested content.

Example with State and Events

@FunctionComponent
def Counter(**props):
    return f"""
    <div {Props.id} {Props.class_name} {Props.style}>
        0 {Props.children}
    </div>
    """

# Create component with initial value "0"
counter = Counter(id="my-counter", children="0")

# Make it reactive controlling the 'text' property (textContent)
# Note: This replaces the entire content of the div with the new text
state = State(counter, text="0")

# Update it
Button("Increment", on_click=state.text.set("5"))

Using Hooks in FunctionComponents

FunctionComponents work seamlessly with all Dars hooks, enabling reactive and interactive behavior.

useDynamic() - Reactive Bindings

Use useDynamic() to create reactive text that updates automatically when state changes:

from dars.all import *

userState = State("user", name="John Doe", status="Active")

@FunctionComponent
def UserCard(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <h3>Name: {useDynamic("user.name")}</h3>
        <p>Status: {useDynamic("user.status")}</p>
        {Props.children}
    </div>
    '''

# The name and status will update automatically when state changes
card = UserCard(id="user-card")
Button("Update", on_click=userState.name.set("Jane Doe"))

useValue() - Initial Values with Selectors

Use useValue() with selectors to set initial values and enable value extraction:

from dars.all import *

app = App("Example of hooks")

userState = State("user", name="Jane Doe", email="jane@example.com", display="None")

@FunctionComponent
def UserForm(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <input value="{useValue("user.name", ".name-input")}" />
        <input value="{useValue("user.email", "#email-field")}" />
        <span>{useValue("user.age", ".age-display")}</span>
        <span>{useDynamic("user.display")}</span>
    </div>
    '''


@route("/")
def index():
    return Page(
        UserForm(id="user-form"),
        # Extract values using V() helper with the selectors
        Button(
            "Get Name",
            on_click=userState.display.set(
                "Name: " + V(".name-input")  # Extract current value
            )
        ),

        Button(
            "Combine Values",
            on_click=userState.display.set(
                V(".name-input") + " (" + V("#email-field") + ")"
            )
        )
    )

app.add_page("index", index(), title="hooks", index=True)

if __name__ == "__main__":
    app.rTimeCompile()

useWatch() - Side Effects

Use useWatch() to monitor state changes and execute side effects:

from dars.all import *

app = App("Example of hooks")

cartState = State("cart", total=0.0)

@FunctionComponent
def CartSummary(total=0,**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <h3>Cart Total: ${useDynamic("cart.total")}</h3>
        {Props.children}
    </div>
    '''

# Watch for cart changes and log
app.useWatch("cart.total", log("Cart total changed!"))

@route("/")
def index():
    return Page(
        CartSummary(id="cart-summary", total=0),
        # Button to add $10 to cart total using V() with state path
        Button("Add $10", on_click=cartState.total.set(
            V("cart.total").float() + 10
        ))
    )

app.add_page("index", index(), title="hooks", index=True)

if __name__ == "__main__":
    app.rTimeCompile()

Combining Multiple Hooks

You can combine multiple hooks for complex interactive components:

from dars.all import *

app = App("Example of hooks")

productState = State("product", 
    name="Widget", 
    price=19.99, 
    quantity=1,
    total=19.99
)

@FunctionComponent
def ProductCard(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <!-- useDynamic for reactive display -->
        <h3>{useDynamic("product.name")}</h3>
        <p>Price: ${useDynamic("product.price")}</p>
        <!-- useValue for editable quantity -->
        <h3>Number to multiply with price</h3>
        <input type="number" 
               value="{useValue("product.quantity", ".qty-input")}"
               min="1" />


        <!-- useDynamic for calculated total -->
        <p>Total: ${useDynamic("product.total")}</p>

        {Props.children}
    </div>
    '''

# Watch for total changes and show alert
app.useWatch("product.total", log("Total updated!"))

@route("/")
def index():
    return Page(
        ProductCard(id="product-card",name="Milk", price=100, quantity=0, total=0 ),
        Button(
            "Calculate Total",
            on_click=productState.total.set(
                V(".qty-input").int() * V("product.price").float()
            )
        )

    )

app.add_page("index", index(), title="hooks", index=True)

if __name__ == "__main__":
    app.rTimeCompile()

Class Components (Legacy)

This is the older method of creating components by inheriting from the Component class. It is more verbose and requires manual handling of rendering logic.

from dars.all import *
from dars.core.component import Component

class CustomComponent(Component):
    def __init__(self, title: str, id: str = None, **props):
        super().__init__(**props)
        self.title = title
        self.id = id
        # Manual event attachment
        self.set_event(EventTypes.CLICK, dScript("console.log('click')"))

    def render(self, exporter: 'Exporter') -> str:
        # Manual children rendering
        children_html = self.render_children(exporter)

        return f'''
        <div class="my-component" id="{self.id}" style="{self.style}">
            <h2>{self.title}</h2>
            <div class="content">
                {children_html}
            </div>
        </div>
        '''

Dars Animation System

Dars provides a powerful and easy-to-use animation system built on top of the Web Animations API. It allows you to add professional-grade animations to your components with simple Python function calls.

Animation Overview

All animations in Dars are dScript objects. This means they: - Run entirely on the client side (zero latency) - Can be assigned to any event handler ( on_click , on_mouseover , etc.) - Can be chained together using .then() or the sequence() helper - Return Promises, allowing for complex orchestration

Animation Quick Start

from dars.all import *

# Simple fade in
button.on_click = fadeIn(id="my-element")

# Chain animations
button.on_click = sequence(
    fadeOut(id="old-panel"),
    fadeIn(id="new-panel")
)

Animation Reference

Fade Animations

Control visibility with opacity transitions.

fadeIn(id, duration=300, easing="ease")

Fades an element in from opacity 0 to 1. Sets display: block automatically.

fadeIn(id="modal", duration=500)

fadeOut(id, duration=300, easing="ease", hide=True)

Fades an element out from current opacity to 0. - hide : If True (default), sets display: none after animation completes.

fadeOut(id="notification", duration=2000, hide=True)

Slide Animations

Move elements into or out of view.

slideIn(id, direction="down", duration=300, easing="ease")

Slides an element into its final position. - direction : "up" , "down" , "left" , "right" (from where it enters)

slideIn(id="sidebar", direction="left", duration=400)

slideOut(id, direction="up", duration=300, easing="ease", hide=True)

Slides an element out of view. - direction : "up" , "down" , "left" , "right" (to where it exits)

slideOut(id="sidebar", direction="left", duration=400)

Scale Animations

Zoom elements in and out.

scaleIn(id, duration=300, easing="ease", from_scale=0.0)

Scales an element up to its natural size (scale 1). - from_scale : Starting scale factor (0.0 to 1.0)

scaleIn(id="popup", from_scale=0.5)

scaleOut(id, duration=300, easing="ease", to_scale=0.0, hide=True)

Scales an element down. - to_scale : Ending scale factor (0.0 to 1.0)

scaleOut(id="popup", to_scale=0.0)

Attention Seekers

Draw user attention to elements.

shake(id, intensity=5, duration=500)

Shakes an element horizontally. Great for error feedback. - intensity : Shake distance in pixels.

shake(id="login-form", intensity=10)

bounce(id, distance=20, duration=600)

Bounces an element vertically. - distance : Bounce height in pixels.

bounce(id="notification-icon", distance=15)

pulse(id, scale=1.1, duration=400, iterations=1)

Pulses an element (scales up and down). - scale : Max scale during pulse. - iterations : Number of pulses. Use "infinite" for continuous pulsing.

# Single pulse
pulse(id="heart-icon")

# Continuous heartbeat
pulse(id="status-dot", iterations="infinite", duration=1000)

Transformations

Rotate and flip elements.

rotate(id, degrees=360, duration=500, easing="ease")

Rotates an element.

rotate(id="refresh-icon", degrees=180)

flip(id, axis="y", duration=600)

Flips an element 180 degrees around an axis. - axis : "x" (horizontal flip) or "y" (vertical flip).

flip(id="card", axis="y")

Property Transitions

Animate specific CSS properties.

colorChange(id, from_color, to_color, duration=500, property="background-color")

Smoothly transitions a color property.

colorChange(id="btn", from_color="#fff", to_color="#f00", property="background-color")

morphSize(id, to_width, to_height, duration=500, easing="ease")

Changes the dimensions of an element.

morphSize(id="panel", to_width="100%", to_height="500px")

Chaining & Sequencing

You can run animations in sequence using the sequence() helper or the .then() method.

Using sequence()

The easiest way to run animations one after another.

from dars.all import sequence, fadeIn, slideIn

button.on_click = sequence(
    fadeIn(id="header"),
    slideIn(id="content", direction="up"),
    fadeIn(id="footer")
)

Using .then()

For more granular control or branching logic.

anim1 = fadeIn(id="box1")
anim2 = slideIn(id="box2")

# Run anim1, then anim2
button.on_click = anim1.then(anim2)

Parallel Animations

To run animations simultaneously, simply trigger them in the same event handler (or use a list of handlers).

# Both start at the same time
button.on_click = [
    fadeIn(id="box1"),
    slideIn(id="box2")
]

SPA Routing in Dars Framework

Dars Framework 1.4.5 introduces a powerful SPA (Single Page Application) routing system that supports nested routes, layouts, and automatic 404 handling.

Basic Routing

To create a basic SPA, you define pages and add them to your app. One page must be designated as the index.

from dars.all import *

app = App(title="My SPA App")

# Create pages
home = Page(Container(Text("Home Page")))
about = Page(Container(Text("About Us")))

# Add pages to app
app.add_page(name="home", root=home, route="/", title="Home", index=True)
app.add_page(name="about", root=about, route="/about", title="About")

you can also use the @route decorator to add pages to your app.

from dars.all import *

app = App(title="My SPA App")

# Create pages
home = Page(Container(Text("Home Page")))
about = Page(Container(Text("About Us")))

# Add pages to app
@app.route("/")
def home():
    return Page(Container(Text("Home Page")))

@app.route("/about")
def about():
    return Page(Container(Text("About Us")))

app.add_page("home", home, title="Home", index=True)
app.add_page("about", about, title="About")

Note: If you use the @route decorator, you can't add the route of the page in app.add_page() .

Nested Routes & Layouts

Nested routes allow you to create layouts that persist while child content changes. This is achieved using the parent parameter and the Outlet component.

The Outlet Component

The Outlet component serves as a placeholder where child routes will be rendered within a parent layout.

from dars.components.advanced.outlet import Outlet

# Parent Layout (Dashboard)
dashboard_layout = Page(
    Container(
        Text("Dashboard Header"),
        # Child routes will render here:
        Outlet(),
        Text("Dashboard Footer")
    )
)

# Child Page (Settings)
settings_page = Page(
    Container(Text("Settings Content"))
)

The Outlet can also render an optional placeholder while the child route is still loading (SSR lazy-load or SPA navigation). If placeholder is not provided, nothing is rendered.

from dars.components.advanced.outlet import Outlet

dashboard_layout = Page(
    Container(
        Text("Dashboard Header"),
        Outlet(
            placeholder=Container(Text("Loading section..."))
        ),
        Text("Dashboard Footer")
    )
)

Multiple Outlets (outlet_id)

You can declare multiple outlets in the same layout by giving each Outlet an outlet_id . Child routes can then target a specific outlet via app.add_page(..., outlet_id="...") .

from dars.components.advanced.outlet import Outlet

dashboard_layout = Page(
    Container(
        Text("Dashboard Header"),
        Container(
            Outlet(outlet_id="main"),
            Outlet(outlet_id="sidebar", placeholder=Text("Loading sidebar...")),
            style={"display": "flex", "gap": "16px"}
        ),
        Text("Dashboard Footer")
    )
)

Configuring Nested Routes

Use the parent parameter in add_page to define the hierarchy.

# 1. Add the parent route
app.add_page(
    name="dashboard", 
    root=dashboard_layout, 
    route="/dashboard", 
    title="Dashboard"
)

# 2. Add the child route, specifying the parent's name
app.add_page(
    name="settings", 
    root=settings_page, 
    route="/dashboard/settings", 
    title="Settings",
    parent="dashboard"  # This links it to the dashboard layout
)

If your parent layout contains multiple outlets, pass outlet_id in the child route to target the correct outlet:

app.add_page(
    name="dashboard",
    root=dashboard_layout,
    route="/dashboard",
    title="Dashboard"
)

app.add_page(
    name="settings",
    root=settings_page,
    route="/dashboard/settings",
    title="Settings",
    parent="dashboard",
    outlet_id="main"
)

When you navigate to /dashboard/settings , Dars will render the dashboard layout and place the settings content inside the Outlet .

Trailing Slashes

The SPA router normalizes paths so that trailing slashes do not create false 404s:

  • /dashboard and /dashboard/ are treated as the same route.
  • The root path / remains / .

404 Handling

Dars provides robust handling for non-existent routes.

Default 404 Page

If a user navigates to a route that doesn't exist, Dars automatically: 1. Redirects the user to /404 . 2. Displays a built-in, clean "404 Page Not Found" error page.

Custom 404 Page

You can customize the 404 page using app.set_404_page() .

# Create your custom 404 page
not_found_page = Page(
    Container(
        Text("Oops! Page not found 😢", style={"fontSize": "32px"}),
        Link("Go Home", href="/")
    )
)

# Register it
app.set_404_page(not_found_page)

Now, when a 404 occurs, users will be redirected to /404 but will see your custom design.

403 Handling

Similar to 404 pages, you can define a custom 403 Forbidden page for unauthorized access to private routes.

Default 403 Page

Dars includes a default 403 page that informs users they don't have permission to access the requested resource.

Custom 403 Page

You can customize the 403 page using app.set_403_page() .

# Create your custom 403 page
forbidden_page = Page(
    Container(
        Text("⛔ Access Denied", style={"fontSize": "32px", "color": "red"}),
        Text("You do not have permission to view this page."),
        Link("Go to Login", href="/login")
    )
)

# Register it
app.set_403_page(forbidden_page)

Dars will automatically redirect to /prohibited and show this page when a user tries to access a private route without authentication.

Hot Reload

The development server ( dars dev ) includes an intelligent hot reload system for SPAs:

  • Automatic Detection : The browser automatically detects changes to your Python code.
  • Smart Polling : It checks for updates every 500ms without spamming your console logs.
  • Retry Limit : If the server goes down, the client stops polling after 10 consecutive errors to prevent browser lag.
  • State Preservation : When possible, navigation state is preserved across reloads.

SEO & Metadata

Dars handles SEO automatically in Single Page Applications. The router intelligently updates the document metadata when navigating between routes.

Using the Head Component

To control page metadata for each route, use the Head component:

from dars.components.advanced.head import Head

@app.route("/about")
def about():
    return Page(
        Head(
            title="About Us - My App",
            description="Learn more about our company.",
            og_image="/images/about-og.jpg"
        ),
        Container(Text("About Content"))
    )

The router dynamically updates: - <title> - Meta tags ( description , keywords , etc.) - Open Graph tags ( og:title , og:type , etc.) - Twitter Cards

This ensures that even client-side routes display the correct information in the browser tab and when shared on social media.


Server-Side Rendering (SSR)

Dars Framework provides complete Server-Side Rendering support integrated with FastAPI, allowing you to build full-stack applications with both server-rendered and client-side pages.

Quick Overview

SSR routes are rendered on the server before being sent to the client, providing: - Faster initial page load - Progressive enhancement - Flexible architecture (mix SSR, SPA, and Static routes)

Basic SSR Route

from dars.all import *
from backend.apiConfig import DarsEnv

# Configure SSR URL
ssr_url = DarsEnv.get_urls()['backend']
app = App(title="My App", ssr_url=ssr_url)

# Define SSR route
@route("/", route_type=RouteType.SSR)
def home():
    return Page(
        Heading("Welcome!", level=1),
        Text("This page is rendered on the server!")
    )

app.add_page("home", home(), title="Home")

Dual Hydration System

Dars uses a sophisticated "Dual Hydration" approach:

  1. Server Side : Renders component to HTML and builds VDOM snapshot
  2. Client Side : Displays server HTML immediately, then hydrates with JavaScript
  3. Result : No flickering, instant content, full interactivity

This prevents Flash of Unstyled Content (FOUC), double rendering, and race conditions.

Creating an SSR Project

Use the Dars CLI to scaffold a complete SSR project with FastAPI backend:

dars init my-ssr-app --type ssr
cd my-ssr-app

This creates a full-stack project with: - Frontend Dars app ( main.py ) - FastAPI backend ( backend/api.py ) - Environment configuration - Development and production setup

Complete SSR Documentation

For comprehensive SSR documentation including: - Architecture and how it works - Development workflow - API reference ( create_ssr_app , SSRRenderer ) - Mixing SSR, SPA, and Static routes - Deployment guide - Advanced features (authentication, custom endpoints) - Best practices and troubleshooting - Real-world examples

See the Complete SSR Guide


Server-Side Rendering in Dars Framework

Dars Framework provides a complete Server-Side Rendering solution integrated with FastAPI, allowing you to build full-stack applications with both server-rendered and client-side pages in a single codebase.

Overview

SSR in Dars renders your pages on the server before sending them to the client, providing:

  • Faster Initial Load : Users see content immediately without waiting for JavaScript
  • Flexible Architecture : Mix SSR and SPA routes in the same application

Architecture

How SSR Works in Dars

  1. Client Request : Browser requests a page (e.g., /dashboard )
  2. Server Rendering : FastAPI backend renders the Dars component to HTML
  3. HTML Response : Server sends fully-rendered HTML with embedded VDOM
  4. Client Hydration : Browser loads JavaScript and "hydrates" the static HTML
  5. Interactive : Page becomes fully interactive with event handlers

Dual Hydration System

Dars uses a sophisticated "Dual Hydration" approach to prevent flickering and ensure smooth transitions:

Server Side:
1. Render component to HTML
2. Build VDOM representation
3. Inject VDOM as window.__ROUTE_VDOM__
4. Send HTML + VDOM to client

Client Side:
1. Display server-rendered HTML (instant)
2. Load dars.min.js runtime
3. Detect __ROUTE_VDOM__ presence
4. Hydrate DOM without re-rendering
5. Attach event handlers and state

This prevents: - Flash of Unstyled Content (FOUC) - Double rendering - Race conditions - Lost event handlers

SEO & Metadata

SSR routes are fully SEO-optimized. Using the Head component allows you to inject metadata directly into the server-rendered HTML.

@route("/blog/post-1", route_type=RouteType.SSR)
def blog_post():
    return Page(
        Head(
            title="My Amazing Blog Post",
            description="Read this incredible story...",
            keywords="blog, story, amazing",
            og_type="article"
        ),
        # ... content ...
    )

Quick Start

Creating an SSR Project

Use the Dars CLI to scaffold a complete SSR project:

dars init my-ssr-app --type ssr
cd my-ssr-app

This creates:

my-ssr-app/
├── main.py              # Frontend (Dars app)
├── backend/
│   ├── api.py          # FastAPI server with SSR
│   └── apiConfig.py    # Environment configuration
└── dars.config.json    # Dars configuration

Project Structure

Frontend ( main.py )

from dars.all import *
from backend.apiConfig import DarsEnv

# Configure SSR URL
ssr_url = DarsEnv.get_urls()['backend']
app = App(title="My SSR App", ssr_url=ssr_url)

# Define SSR route
@route("/", route_type=RouteType.SSR)
def index():
    return Page(
        Text("Hello from Server!", style="fs-[32px]"),
        Button("Click Me", on_click=alert("Interactive!"))
    )

app.add_page("index", index(), title="Home")

if __name__ == "__main__":
    app.rTimeCompile()

Backend ( backend/api.py )

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from dars.backend.ssr import create_ssr_app
from apiConfig import DarsEnv

# Import Dars app
import sys
sys.path.insert(0, '.')
from main import app as dars_app

# Create FastAPI app with SSR
app = create_ssr_app(dars_app)

# Enable CORS for development
if DarsEnv.is_dev():
    urls = DarsEnv.get_urls()
    app.add_middleware(
        CORSMiddleware,
        allow_origins=[urls['frontend']],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=3000)

Environment Config ( backend/apiConfig.py )

class DarsEnv:
    MODE = "development"  # or "production"

    DEV = "development"
    BUILD = "production"

    @staticmethod
    def is_dev():
        return DarsEnv.MODE == DarsEnv.DEV

    @staticmethod
    def get_urls():
        if DarsEnv.is_dev():
            return {
                "backend": "http://localhost:3000",
                "frontend": "http://localhost:8000"
            }
        return {
            "backend": "/",
            "frontend": "/"
        }

Route Types

Dars supports two routing modes that can be mixed in the same application:

1. SSR Routes ( RouteType.SSR )

Server-rendered on every request.

@route("/dashboard", route_type=RouteType.SSR)
def dashboard():
    return Page(
        Text("Dashboard", level=1),
        Text(f"Rendered at: {datetime.now()}")
    )

When to use: - SEO-critical pages (landing pages, blog posts) - Dynamic content that changes frequently - Pages requiring authentication checks server-side - Initial load performance is critical

2. SPA Routes ( RouteType.PUBLIC ) - Default

Client-side navigation, no server rendering.

@route("/settings")  # Default is PUBLIC
def settings():
    return Page(
        Text("Settings", level=1),
        # Interactive forms, real-time updates
    )

When to use: - Admin dashboards (not recommended for now in the future will be supported with Dars Middleware) - Interactive tools - Pages behind authentication - Real-time applications

SSR Lazy-Load Placeholders (SPA Navigation)

When you navigate to an RouteType.SSR route from the SPA router, the client fetches route data from the backend ( /api/ssr/... ). You can configure global loading and error placeholders for this lazy-load step:

app.set_loading_state(
    loadingComp=Page(Container(Text("Loading..."))),
    onErrorComp=Page(Container(Text("Failed to load route")))
)

These placeholders are rendered as static HTML (similar to SPA 404/403 pages), which means they do not register states/events and do not interfere with hydration.

Nested Layouts and Outlet(placeholder=...)

For nested routes, layouts typically include one or more Outlet placeholders. You can optionally render a layout-level placeholder inside an outlet:

Outlet(outlet_id="main", placeholder=Container(Text("Loading section...")))

This is useful when the parent layout is already visible and you want a placeholder only for the child region.


Development Workflow

Running in Development

You need two processes running simultaneously:

Terminal 1 - Frontend Dev Server:

dars dev
# Runs on http://localhost:8000
# Uses app.rTimeCompile() with hot reload for UI changes

Terminal 2 - Backend SSR Server:

dars dev --backend
# Runs on http://localhost:3000
# Starts uvicorn with the backendEntry from dars.config.json (by default "backend.api:app")

How It Works

  1. Frontend Server (8000) : Serves the Dars preview (HTML/CSS/JS) and handles SPA routing.
  2. Backend Server (3000) : Renders SSR routes and provides API endpoints.

  3. Communication : Frontend fetches SSR content from backend via /api/ssr/*

Environment Detection

The DarsEnv class automatically configures URLs:

# Development
DarsEnv.get_urls()  {
    "backend": "http://localhost:3000",
    "frontend": "http://localhost:8000"
}

# Production
DarsEnv.get_urls()  {
    "backend": "/",
    "frontend": "/"
}

SSR API Reference

create_ssr_app(dars_app, prefix="/api/ssr", streaming=False)

Creates a FastAPI application with automatic SSR endpoints.

Parameters: - dars_app (App): Your Dars application instance - prefix (str): URL prefix for SSR endpoints (default: /api/ssr ) - streaming (bool): When True , enables HTML streaming so the <head> and opening <body> are sent first, and the rest of the document is streamed afterwards. Default is False (classic non-streaming response).

Returns: - FastAPI application with registered SSR routes

Auto-generated Endpoints:

For each SSR route in your Dars app, create_ssr_app creates: - GET {prefix}/{route_name} - JSON payload used by the SPA router for lazy SSR loading - GET {route_path} - Full HTML SSR endpoint (e.g. / , /blog , /dashboard )

Additionally, if no SSR route takes / , a health-check endpoint is added at: - GET / - Returns basic JSON info about the SSR backend

Example (non-streaming):

from dars.backend.ssr import create_ssr_app

app = create_ssr_app(dars_app)
# Automatically creates, for example:
# - GET /api/ssr/index
# - GET /api/ssr/dashboard
# - GET /           (if root not taken by an SSR route)

Example (streaming enabled):

from dars.backend.ssr import create_ssr_app

app = create_ssr_app(dars_app, streaming=True)
# HTML responses for SSR routes are sent in two chunks:
# 1) <html> + <head> + opening <body>
# 2) The rest of the document (body content + scripts)

Behind the scenes, create_ssr_app uses SSRRenderer to: - Render the SSR route to HTML and wrap it in __dars_spa_root__ for hydration. - Build a minimal SPA config and expose it as window.__DARS_SPA_CONFIG__ . - Serialize initial state snapshots: - V1: window.__DARS_STATE__ (STATE_BOOTSTRAP) - V2: window.__DARS_STATE_V2__ (STATE_V2_REGISTRY via to_dict() ) - Inject a VDOM snapshot as window.__ROUTE_VDOM__ .

SSRRenderer

Low-level class for manual SSR rendering.

from dars.backend.ssr import SSRRenderer

renderer = SSRRenderer(dars_app)
result = renderer.render_route("dashboard", params={"user_id": "123"})

# Returns (simplified):
{
    "name": "dashboard",
    "html": "<div>...</div>",              # Body HTML for SPA hydration
    "fullHtml": "<!DOCTYPE html>...",      # Complete HTML document with <head>
    "scripts": [...],                       # Core + page-specific scripts
    "events": {...},                        # Event map for client-side binding
    "vdom": {...},                          # VDOM snapshot for the route
    "states": [...],                        # V1 state snapshot (STATE_BOOTSTRAP)
    "statesV2": [...],                      # V2 state snapshot (STATE_V2_REGISTRY)
    "spaConfig": {...},                     # Minimal SPA routing config
    "headMetadata": {...}                   # Metadata extracted from Head component
}
app = App(title="Hybrid App", ssr_url=ssr_url)

# SSR for landing page (SEO)
@route("/", route_type=RouteType.SSR)
def home():
    return Page(Text("Welcome!"))

# SPA for dashboard (interactive)
@route("/dashboard")
def dashboard():
    return Page(Text("Dashboard"))

# Static for docs (performance)
@route("/docs", route_type=RouteType.STATIC)
def docs():
    return Page(Text("Documentation"))

app.add_page("home", home(), title="Home", index=True)
app.add_page("dashboard", dashboard(), title="Dashboard")
app.add_page("docs", docs(), title="Docs")

Navigation Behavior: - SSR → SPA: Fetches from backend, hydrates - SPA → SPA: Client-side navigation (instant) - Any → Static: Loads pre-rendered HTML


Deployment

Production Configuration

1. Update Environment Mode:

# backend/apiConfig.py
class DarsEnv:
    MODE = "production"  # Change from "development"

2. Build Frontend:

dars build
# Generates static files in ./dist

3. Deploy Backend:

Your FastAPI backend serves both: - SSR-rendered pages via /api/ssr/* - Static files from ./dist

Example Production Server:

# backend/api.py
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from dars.backend.ssr import create_ssr_app
from main import app as dars_app

app = create_ssr_app(dars_app)

# Serve static files
app.mount("/", StaticFiles(directory="dist", html=True), name="static")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Deployment Platforms

Vercel / Netlify: - Deploy FastAPI backend as serverless function - Serve static files from CDN - Configure environment variables

State Management in Dars

Dars Framework features powerful state management systems , designed for different use cases:

State V2 - Dynamic State Management

Modern, Pythonic state management for reactive UIs.

Quick Start

from dars.all import *

# Create a component
display = Text("0", id="counter")

# Create state
counter = State(display, text=0)

# Use reactive properties
increment_btn = Button("Increment", on_click=counter.text.increment(by=1))
decrement_btn = Button("Decrement", on_click=counter.text.decrement(by=1))
reset_btn = Button("Reset", on_click=counter.reset())

Core Concepts

State Class

The State class wraps a component and provides reactive property access.

from dars.all import State

display = Text("0", id="counter")
counter_state = State(display, text=0)

Constructor Parameters: - component : The component to manage (can be a component object or string ID) - **default_props : Default property values (e.g., text=0 , style={...} )

State with String IDs (for Dynamic Components)

State() can accept either a component object or a string ID. This is useful for components created dynamically:

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: - Components created with createComp() - Dynamically generated UIs - Conditional component rendering - Server-side rendered components

Reactive Properties

Access component properties through the state object to get reactive operations:

# Increment/decrement numeric properties
counter.text.increment(by=1)
counter.text.decrement(by=2)

# Set property values
counter.text.set(value=100)

# Auto operations (continuous)
counter.text.auto_increment(by=1, interval=1000)  # +1 every second
counter.text.auto_decrement(by=1, interval=500)   # -1 every 500ms
counter.text.stop_auto()  # Stop auto operations

Reset to Defaults

The reset() method restores all properties to their initial values:

state = State(display, text=0, style={"color": "blue"})

# ... user modifies the component ...

# Reset everything back to initial state
reset_btn.on_click = state.reset()

Reactive Operations

Increment and Decrement

# Increment by 1 (default)
button.on_click = counter.text.increment()

# Increment by custom amount
button.on_click = counter.text.increment(by=5)

# Decrement (negative increment)
button.on_click = counter.text.decrement(by=1)
# OR
button.on_click = counter.text.increment(by=-1)
button.on_click = counter.text.set(value=0)

All Property Types Supported

State V2 supports updating all component properties , not just text:

Text Content:

state.text.set("New text")

HTML Content:

state.html.set("<strong>Bold text</strong>")

CSS Styles:

state.style.set({"color": "red", "fontSize": "24px"})

CSS Classes:

# Set class name
state.class_name.set("active")

Event Handlers:

# Update event handler dynamically
state.update(on_click=alert("New handler!"))

# Or with dScript
from dars.scripts.dscript import dScript
state.update(on_click=dScript("console.log('clicked')"))

Multiple Properties at Once:

state.update(
    text="Updated!",
    class_name="success",
    style={"color": "green"},
    on_click=alert("Done!")
)

Auto Operations

Auto operations create continuous reactive updates:

# Auto-increment timer
timer = State(display, text=0)

start_btn.on_click = timer.text.auto_increment(by=1, interval=1000)
stop_btn.on_click = timer.text.stop_auto()

With Limits:

# Auto-increment up to 100
timer.text.auto_increment(by=1, interval=1000, max=100)

# Auto-decrement down to 0
countdown.text.auto_decrement(by=1, interval=1000, min=0)

Backend Integration with useData()

State V2 integrates seamlessly with Dars backend HTTP utilities for reactive API-driven UIs:

from dars.all import *
from dars.backend import get, useData

# Create components
user_name = Text("", id="user-name")
user_email = Text("", id="user-email")

# Create states
name_state = State(user_name, text="")
email_state = State(user_email, text="")

# Fetch and bind API data - pure Python!
fetch_btn = Button(
    "Load User",
    on_click=get(
        id="userData",
        url="https://api.example.com/users/1",
        # Access nested data with dot notation
        callback=(
            name_state.text.set(useData('userData').name)
            .then(email_state.text.set(useData('userData').email))
        )
    )
)

Key Features: - useData('id') - Access fetched data by operation ID - Dot notation - useData('userData').name accesses nested properties - .then() chaining - Chain multiple state updates sequentially - No JavaScript - Everything is pure Python

Complete Example

from dars.all import *

app = App("State V2 Demo")

# Timer display
timer_display = Text("0", id="timer", style={"font-size": "36px"})
timer = State(timer_display, text=0)

# Status display
status = Text("Paused", id="status")
status_state = State(status, text="Paused", class_name="paused")

# Control buttons
start_btn = Button("Start",
    on_click=timer.text.auto_increment(by=1, interval=1000)
)
stop_btn = Button("Stop", 
    on_click=[
        timer.text.stop_auto(),
        status_state.update(text="Paused", class_name="paused")
    ]
)
reset_btn = Button("Reset",
    on_click=[
        timer.text.stop_auto(),
        timer.reset(),
        status_state.reset()
    ]
)

page = Page(Container(timer_display, status, start_btn, stop_btn, reset_btn))
app.add_page("index", page, index=True)

if __name__ == "__main__":
    app.rTimeCompile()

Dynamic State Updates & this()

Dars introduces dynamic state updates, allowing you to modify component properties directly without pre-registering state indices.

this() helper

The this() helper allows a component to refer to itself in an event handler and apply updates dynamically.

from dars.core.state import this

btn = Button("Click me", on_click=this().state(text="Clicked!", style={"color": "red"}))

Supported dynamic properties: - text : Update text content. - html : Update inner HTML. - style : Dictionary of CSS styles. - attrs : Dictionary of attributes. - classes : Dictionary with add , remove , or toggle (single string or list of strings).

this().state(
    text="Updated",
    style={"backgroundColor": "#f0f0f0"},
    classes={"add": ["active"], "remove": ["inactive"]}
)

Using Raw JavaScript Values ( RawJS )

You can pass raw JavaScript variables to dynamic updates using RawJS . This is particularly useful when: - Chaining scripts where a previous script returns a value - Working with async operations like file reading - Using dScript.ARG to reference values from previous scripts

from dars.scripts.dscript import RawJS, dScript

# Using dScript.ARG placeholder for chained values
this().state(text=RawJS(dScript.ARG))

# Using custom JavaScript expressions
this().state(text=RawJS("someVar + ' processed'"))

Best Practices

Choose State V2 When:

  • Building simple counters or timers
  • Need auto-increment/decrement
  • Want quick reactive updates
  • Working with single components

Use this() When:

  • Don't need state tracking
  • Making one-off updates
  • Working with async operations
  • Targeting the clicked element

Hooks System

Dars Framework introduces a Hooks system inspired by React, enabling reactive and stateful behavior in both FunctionComponents and built-in components.

Overview Hooks

Hooks provide a way to add reactive capabilities to your application. They enable features like:

  • Reactive state bindings - Automatically update UI when data changes
  • State monitoring - Watch for state changes and execute side effects
  • External state integration - Connect components to global state

Important: State ID Best Practices

[!IMPORTANT] When using State objects with hooks like useDynamic and useValue , 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.

Why This Matters

The reactive system uses watchers to update components when state changes. When you create a State object, the ID you provide is used to register the state in the internal registry, not to identify a specific DOM element.

Examples

X Incorrect - State ID matches component ID:

# DON'T do this
state = State("my-button", count=0, disabled=False)
Button(id="my-button", text=useDynamic("my-button.count"))

In this example, both the state and the button have the ID "my-button" , which can cause confusion and unexpected behavior.

✓ Correct - State has unique ID:

# DO this - give state a descriptive, unique ID
counter_state = State("counter-state", count=0, disabled=False)
Button(id="my-button", text=useDynamic("counter-state.count"))
Button(id="another-button", disabled=useDynamic("counter-state.disabled"))

✓ Also Correct - Multiple components sharing same state:

# One state can control multiple components
ui_state = State("ui", count=0, is_disabled=False, message="Hello")

Container(
    Text(text=useDynamic("ui.message")),
    Button(id="btn-1", disabled=useDynamic("ui.is_disabled")),
    Button(id="btn-2", disabled=useDynamic("ui.is_disabled")),
    Text(text=useDynamic("ui.count"))
)

Key Takeaways

  1. State IDs are for the state object , not for DOM elements
  2. One state can control many components through reactive bindings
  3. Component IDs should be unique across your DOM
  4. State IDs should be descriptive of what they manage (e.g., "user-data" , "cart-state" , "ui-controls" )

useValue() - Initial Value Access

The useValue() hook allows you to access the initial value of a state property without creating a reactive binding. This is ideal for form inputs where you want to set a default value but allow the user to edit it freely.

Basic Usage

Pass useValue() to component properties to set their initial value from state:

from dars.all import *

userState = State("user", name="John Doe", email="john@example.com")

# Input with initial value from state (editable by user)
Input(value=useValue("user.name"))

# Textarea with initial value
Textarea(value=useValue("user.email"))

Usage in FunctionComponents with Selectors

useValue() supports automatic selector application in FunctionComponents! When you provide a selector (class or ID), it will be automatically applied to the element where the value is used.

from dars.all import *

app = App("Example of hooks")

userState = State("user", name="Jane Doe", email="jane@example.com", display="None")

@FunctionComponent
def UserForm(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <input value="{useValue("user.name", ".name-input")}" />
        <input value="{useValue("user.email", "#email-field")}" />
        <span>{useValue("user.age", ".age-display")}</span>
        <span>{useDynamic("user.display")}</span>
    </div>
    '''


@route("/")
def index():
    return Page(
        UserForm(id="user-form"),
        # Extract values using V() helper with the selectors
        Button(
            "Get Name",
            on_click=userState.display.set(
                "Name: " + V(".name-input")  # Extract current value
            )
        ),

        Button(
            "Combine Values",
            on_click=userState.display.set(
                V(".name-input") + " (" + V("#email-field") + ")"
            )
        )
    )

app.add_page("index", index(), title="hooks", index=True)

if __name__ == "__main__":
    app.rTimeCompile()

How it works: 1. useValue("user.name", ".name-input") sets initial value "Jane Doe" and applies class name-input to the input 2. User can edit the value freely 3. V(".name-input") extracts the current value (even if modified by user) 4. Perfect for forms where you need both initial values and value extraction

Supported selectors: - Class selectors ( .foo ) → Added to element's class attribute - ID selectors ( #bar ) → Set as element's id attribute

Difference from useDynamic

  • useDynamic("state.prop") : Creates a reactive binding . If the state changes, the input value updates automatically.
  • useValue("state.prop") : Sets the initial value only . If the state changes later, the input value does NOT update. This prevents overwriting user input while they are typing.

Syntax

useValue(state_path: str, selector: str = None) -> ValueMarker

Parameters: - state_path : Dot-notation path to state property (e.g., "user.name" ) - selector : Optional CSS selector (class or ID) to apply to the element

Returns: - ValueMarker object that resolves to the initial value during component rendering.


useDynamic() - Reactive State Binding

The useDynamic() hook creates reactive bindings between external State objects and component properties.

1. Usage in Built-in Components

You can pass useDynamic() directly to properties of built-in components like Text , Button , Input , etc.

from dars.all import *

# Create state
userState = State("user", name="John Doe", status="Active", is_admin=False)

# Bind directly to props
card = Container(
    # Bind text property
    Text(text=useDynamic("user.name"), style={"font-weight": "bold"}),

    # Bind input value
    Input(value=useDynamic("user.name"), placeholder="Edit name"),

    # Bind button text and disabled state
    Button(
        text=useDynamic("user.status"), 
        disabled=useDynamic("user.is_admin"),
        on_click=userState.status.set("Clicked!")
    )
)

Supported Properties

useDynamic and useValue supports binding to the following properties on built-in components:

Component Properties
Text text , innerHTML
Button text , disabled
Input value , placeholder , disabled , readonly , required
Textarea value , placeholder , disabled , readonly , required
Image src , alt
Link href , text
Checkbox checked , disabled , required
RadioButton checked , disabled , required
Select disabled , required
Slider disabled

Boolean attributes like disabled and checked will be toggled based on the truthiness of the state value.

2. Usage in FunctionComponents

You can also use useDynamic() within FunctionComponent templates to create reactive spans.

@FunctionComponent
def UserCard(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <h3>Name: {useDynamic("user.name")}</h3>
        <p>Status: {useDynamic("user.status")}</p>
    </div>
    '''

Syntax

useDynamic(state_path: str) -> DynamicBinding

Parameters: - state_path : Dot-notation path to state property (e.g., "user.name" , "cart.total" )

Returns: - DynamicBinding object that resolves to the current value during render and updates automatically when state changes.


useWatch() - State Monitoring

The useWatch() hook allows you to monitor state changes and execute callbacks (side effects). It supports watching single or multiple state properties and executing one or more callbacks.

Basic Usage

The recommended way to use useWatch is via the app.useWatch() or page.useWatch() methods:

Single State Property

from dars.all import *

cartState = State("cart", count=0, total=0.0)

# Logs to console whenever cart.count changes
app.useWatch("cart.count", log("Cart updated!"))
app.useWatch("cart.total", log("Total changed"))

Multiple State Properties (Array Syntax)

productState = State("product", name="Widget", price=19.99, info="")

# 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")
)

Page-Specific Watchers (page.useWatch)

@route("/cart")
def cart_page():
    page = Page()

    # This watcher only runs on the cart page
    page.useWatch("cart.total", log("Total changed!"))

    page.add(
        Container(
            Text(useDynamic("cart.total"))
        )
    )
    return page

You can also use the classic syntax with add_script :

app.add_script(useWatch("state.prop", log("Changed!")))

Syntax

useWatch(
    state_path: Union[str, List[str]], 
    *callbacks: Union[dScript, str, Callable]
) -> Union[dScript, WatchMarker]

Parameters: - state_path : State property path(s) to watch. Can be: - Single path string (e.g., "user.name" ) - List of paths (e.g., ["product.name", "product.price"] ) - *callbacks : One or more callbacks to execute when state changes. Each can be: - dScript object (e.g., log("Changed") , alert("Update") ) - State setter (e.g., productState.info.set(...) ) - Inline JavaScript string - Python callable returning a dScript

Behavior: - When using an array of state paths, the callback(s) execute when any of the watched properties change - Multiple callbacks execute in the order they are provided - Callbacks can access current state values using V() helper


Pythonic Value Helpers

Dars provides a set of helpers to make working with DOM values and reactive state completely Pythonic, eliminating the need for raw JavaScript.

V() - Value Reference

The V() helper allows you to extract values from DOM elements (via CSS selectors) or reactive state (via state paths).

CSS Selectors (DOM Elements)

from dars.all import *

# Select by ID
V("#myInput")

# Select by Class
V(".myClass")

State Paths (Reactive State)

New in v1.5.8 : V() now supports extracting values directly from reactive state created by useDynamic() :

# Extract from reactive state
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") - Reads its current textContent value - Perfect for combining reactive state with calculations

Transformations

You can chain transformation methods to process values before using them:

# String transformations
V("#name").upper()   # "JOHN"
V("#name").lower()   # "john"
V("#name").trim()    # Remove whitespace

# Numeric transformations (required for math operations!)
V("#age").int()      # 25 (integer)
V("#price").float()  # 19.99 (float)
V("cart.total").float()  # Extract state value as float

Operations

V() now supports declarative mathematical expressions with operator overloading!

# Simple arithmetic
calc.result.set(V(".a").float() + V(".b").float())

# Complex expressions with automatic precedence
calc.result.set(
    (V(".a").float() + V(".b").float()) * V(".c").float()
)

# Dynamic operators from Select elements
calc.result.set(
    V(".num1").float() + V(".operation").operator() + V(".num2").float()
)

Features: - Operator overloading ( + , - , * , / , % , ** ) - Automatic operator precedence - Dynamic operators from Select/Input - NaN validation with console warnings - Type safety (numeric ops require .float() or .int() )

[!TIP] For complete documentation on mathematical operations, operator precedence, dynamic operators, and advanced examples, see the Mathematical Operations docs.

equal() helper

Sometimes you want to normalize a value (literal or expression) to safely combine it within an expression with V() without worrying about precedence or operators:

from dars.hooks.value_helpers import V, equal

# Add 1 using V() + literal
updateVRef(".dyn_count", V(".dyn_count").int() + 1)

# Normalize a literal as a mathematical expression
updateVRef(".dyn_count", equal(0))           # forces to 0

# Combine with another expression based on V()
expr = V(".a").int() + equal(V(".b").int())
updateVRef(".result", expr)
  • equal(value) wraps the value in a MathExpression , so it integrates into the same operation tree as V() and respects the async/NaN-safe semantics of the expression system.

Complete Example

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()

url() - URL Builder

The url() helper constructs dynamic URLs by interpolating ValueRef objects into a template string.

# Generates: https://api.example.com/users/123/profile
fetch(
    url("https://api.example.com/users/{id}/profile", id=V("#userId"))
)

# With state values
fetch(
    url("/api/products/{id}", id=V("product.id"))
)

# Mixed
fetch(
    url("/api/{resource}/{id}", 
        resource="users", 
        id=V("#userId"))
)

Note: Use standard Python format string syntax {key} for placeholders.

Boolean & Comparison Operators

V() supports boolean and comparison operations, enabling declarative validation and conditional logic without raw JavaScript!

Comparison Operators

Compare values using Python-style operators:

from dars.all import *

# 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: ==, !=, >, <, >=, <=

String Methods

Check string properties with built-in methods:

# 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()

Logical Operators

Combine boolean expressions with .and_() and .or_() :

# 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(".")
    )
)

Conditional Expressions

Use .then() for ternary operations (condition ? trueVal : falseVal):

# Simple conditional
(V("#age").int() >= 18).then("Adult", "Minor")

# With state updates
state.message.set(
    (V("#score").int() >= 60).then("Pass", "Fail")
)

# Nested conditionals
(V("#premium").bool()).then("10% discount", "No discount")

# Complex validation
state.validation.set(
    (V("#password").length() >= 8).and_(
        V("#password") == V("#confirm")
    ).then("✓ Valid", "✗ Invalid")
)

Complete Validation Example

from dars.all import *

app = App("Form Validation")
form = State("form", 
    email_valid="",
    age_valid="",
    password_valid=""
)

@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 email", "✗ Invalid email")
                )
            ),
            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 age", "✗ Must be 18-120")
                )
            ),
            Text(text=useDynamic("form.age_valid")),

            # Password match validation
            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("✓ Passwords match", "✗ Passwords don't match")
                )
            ),
            Text(text=useDynamic("form.password_valid"))
        )
    )

app.add_page("index", index())

Form Collection System

Pythonic form data collection and submission without raw JavaScript!

FormData & collect_form()

The FormData class and collect_form() helper provide a declarative way to collect form data using V() expressions.

Basic Usage

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))

Advanced Features

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()
)

Alternative Syntaxes:

# Using tuples
form_data = collect_form(
    ("name", V("#name")),
    ("email", V("#email"))
)

# Using dict
form_data = collect_form({
    "name": V("#name"),
    "email": V("#email")
})

FormData Methods

.alert(title) - Show form data in alert dialog:

Button("Show Data", on_click=form_data.alert("Form Data"))

.log(message) - Log form data to console:

Button("Log Data", on_click=form_data.log("Form submitted"))

.to_state(property) - Save form data to state:

Button("Save", on_click=form_data.to_state(state.form_data))

.submit(url, state_property, on_success, on_error) - Submit to backend:

# Simple submit
Button("Submit", on_click=form_data.submit("http://localhost:3000/submit"))

# With state and callbacks
Button("Submit", on_click=form_data.submit(
    url="http://localhost:3000/submit",
    state_property=state.response,
    on_success=alert("Success!"),
    on_error=alert("Error!")
))

.submit_and_alert(state_property, title) - Submit with alert:

Button("Submit", on_click=form_data.submit_and_alert(
    state.data,
    "Form Submitted!"
))

Backend Integration Example

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
            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())

setVRef() - Independent Value Reference

The setVRef() hook allows you to define initial values that are tied to a specific CSS selector. This is the foundation for creating component-level state that can be shared across multiple components without using global State objects.

Basic Usage

Create a reference with an initial value and a selector, then pass it to components.

from dars.all import *

# Create a reference tied to the "#count" ID
count_ref = setVRef(0, "#count")

# Use it in a component
Text(count_ref, id="count")

Shared Values (Multi-Component Updates)

By using a class selector , you can share the same value across multiple components and update them all simultaneously!

# Create a reference tied to a CLASS selector
price_ref = setVRef(99.99, ".product-price")

# Use in multiple places
Container(
    Text("Price: $", style="font-bold"),
    Text(price_ref, class_name="product-price"),  # Main display

    Container(
        Text("Also shown here: $"),
        Text(price_ref, class_name="product-price")   # Secondary display
    )
)

# Update ALL elements matching ".product-price" at once
Button("Discount", on_click=updateVRef(".product-price", 49.99))

Usage in FunctionComponents

setVRef works seamlessly with @FunctionComponent . The value is resolved internally, so your templates remain clean.

@FunctionComponent
def UserBadge(name_ref, **props):
    return f'''
    <div {Props.class_name} {Props.style}>
        User: <span class="user-name">{name_ref}</span>
    </div>
    '''

# Define ref
user_ref = setVRef("Guest", ".user-name")

# Render
UserBadge(user_ref, class_name="badge")

# Update
Button("Login", on_click=updateVRef(".user-name", "John Doe"))

Syntax

setVRef(initial_value: Any, selector: str) -> VRefValue

Parameters: - initial_value : The initial value to display (string, number, boolean). - selector : The CSS selector (ID or Class) that identifies the element(s). - Use #id for single elements. - Use .class for multiple elements sharing the value.

Returns: - VRefValue : An object representing the value, ready to be passed to components.


updateVRef() - Component-Level State Updates

Update DOM element values declaratively without State objects!

The updateVRef() function completes the component-level state management cycle, providing a Pythonic way to update values alongside V() for reading and boolean operators for validation.

The Complete Cycle: 1. Read : V("#input") - Extract values 2. Validate : V("#input").length() >= 3 - Boolean validation
3. Update : updateVRef("#input", "new value") - Update values ✨ NEW!

Basic Usage

from dars.all import *

# Update text content
Button("Set Name", on_click=updateVRef("#name", "John Doe"))

# Update input value
Button("Clear Email", on_click=updateVRef("#email", ""))

# Update checkbox
Button("Check Box", on_click=updateVRef("#agree", True))

# Update number
Button("Set Price", on_click=updateVRef("#price", 99.99))

With V() Expressions

Combine updateVRef() with V() expressions for dynamic updates:

# Copy values between elements
Button("Copy", on_click=updateVRef("#target", V("#source")))

# With transformations
Button("Uppercase", on_click=updateVRef("#output", V("#input").upper()))

# With calculations
Button("Calculate Total", on_click=updateVRef("#total",
    V("#price").float() * V("#qty").int()
))

# With string concatenation
Button("Generate Full Name", on_click=updateVRef("#full-name",
    V("#first-name") + " " + V("#last-name")
))

With Boolean Expressions

Use boolean operators for conditional updates:

# Conditional text based on age
Button("Check Age", on_click=updateVRef("#status",
    (V("#age").int() >= 18).then("Adult", "Minor")
))

# Validation messages
Button("Validate Email", on_click=updateVRef("#message",
    (V("#email").includes("@")).and_(
        V("#email").includes(".")
    ).then("✓ Valid email", "✗ Invalid email")
))

# Complex validation
Button("Check Password", on_click=updateVRef("#pwd-status",
    (V("#password").length() >= 8).and_(
        V("#password") == V("#confirm")
    ).then("✓ Passwords match", "✗ Passwords don't match")
))

Batch Updates

Update multiple elements with a single call:

# Clear entire form
Button("Clear All", on_click=updateVRef({
    "#name": "",
    "#email": "",
    "#age": "",
    "#phone": ""
}))

# Fill sample data
Button("Fill Sample Data", on_click=updateVRef({
    "#name": "John Doe",
    "#email": "john@example.com",
    "#age": 25,
    "#phone": "555-0123"
}))

# Mix literals and expressions
Button("Update All", on_click=updateVRef({
    "#full-name": V("#first") + " " + V("#last"),
    "#email-lower": V("#email").lower(),
    "#age-status": (V("#age").int() >= 18).then("Adult", "Minor")
}))

Complete Examples

Example 1: Counter (No State Object!)

from dars.all import *

app = App("Counter Demo")

@route("/")
def index():
    return Page(
        Container(
            # Display count
            Text("Count: ", style="font-bold"),
            Text("0", id="count", style="text-[48px] font-bold text-blue-600"),

            # Update buttons
            Container(
                Button(
                    "+",
                    on_click=updateVRef("#count", V("#count").int() + 1),
                    style="bg-green-500 text-white px-6 py-3 rounded"
                ),
                Button(
                    "-",
                    on_click=updateVRef("#count", V("#count").int() - 1),
                    style="bg-red-500 text-white px-6 py-3 rounded"
                ),
                Button(
                    "Reset",
                    on_click=updateVRef("#count", 0),
                    style="bg-gray-500 text-white px-6 py-3 rounded"
                ),
                style="flex gap-2"
            )
        )
    )

app.add_page("index", index())

Example 2: Form Auto-Fill

@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")
                )
            ),

            # Normalize inputs
            Button(
                "Normalize All",
                on_click=updateVRef({
                    "#first-name": V("#first-name").trim(),
                    "#last-name": V("#last-name").trim()
                })
            ),

            # Clear all
            Button(
                "Clear All",
                on_click=updateVRef({
                    "#first-name": "",
                    "#last-name": "",
                    "#full-name": ""
                })
            )
        )
    )

Example 3: Shopping Cart

@route("/cart")
def cart():
    return Page(
        Container(
            Input(id="price", input_type="number", value="19.99", placeholder="Price"),
            Input(id="quantity", input_type="number", value="1", placeholder="Quantity"),

            Text("Total: $", style="font-bold"),
            Text("0", id="total", style="text-[24px] text-green-600"),

            # 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
                )
            ),

            # Reset
            Button(
                "Reset",
                on_click=updateVRef({
                    "#price": "19.99",
                    "#quantity": "1",
                    "#total": "0"
                })
            )
        )
    )

Syntax

updateVRef(selector, value) -> dScript
updateVRef(dict) -> dScript

Parameters: - selector : CSS selector string (e.g., "#id" , ".class" ) - value : Value to set - can be: - Literal: "text" , 42 , True - V() expression: V("#source") - Transformation: V("#input").upper() - Math expression: V("#a").int() + V("#b").int() - Boolean expression: (V("#age").int() >= 18).then("Adult", "Minor") - dict : Dictionary of {selector: value} pairs for batch updates

Returns: - dScript object for use in event handlers

Supported Elements: - Input / Textarea : Updates .value property - Checkbox / Radio : Updates .checked property - Select : Updates .value property - Other elements: Updates .textContent

Integration with Other Features

With collect_form()

# Normalize before collecting
form_data = collect_form(
    name=V("#name"),
    email=V("#email")
)

Button(
    "Normalize & Submit",
    on_click=sequence(
        updateVRef({
            "#name": V("#name").trim(),
            "#email": V("#email").lower().trim()
        }),
        form_data.submit("http://localhost:3000/submit")
    )
)

With State (Hybrid Approach)

# Local updates for preview
Button("Preview", on_click=updateVRef("#preview",
    "Name: " + V("#name") + ", Email: " + V("#email")
))

# Save to global state
user = State("user", name="", email="")
Button("Save to State", on_click=sequence(
    user.name.set(V("#name")),
    user.email.set(V("#email"))
))

When to Use updateVRef() vs State.set()

Use updateVRef() when: - Updating UI elements temporarily - Form auto-fill and normalization - Local calculations and previews - Component-level state - You don't need reactivity across components

Use State.set() when: - Data needs to persist - Multiple components need the value - You need automatic reactivity - Application-level state

Use both (Hybrid): - Local updates for immediate feedback - State updates for persistence - Best of both worlds!


getDateTime() - Timestamp Helper

**Generate client-side timestamps for forms and state updates.

Basic Usage

from dars.all import *

# 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

# Add timestamp to form submission
form_data = collect_form(
    name=V("#name"),
    email=V("#email"),
    submitted_at=getDateTime()  # ISO format
)

# Different timestamp formats
form_data = collect_form(
    name=V("#name"),
    created_at=getDateTime("iso"),
    display_date=getDateTime("locale"),
    date_only=getDateTime("date"),
    time_only=getDateTime("time"),
    unix_timestamp=getDateTime("timestamp")
)

Usage with State

# Update state with current timestamp
Button("Save", on_click=state.last_updated.set(getDateTime()))

# Different formats
Button("Save Date", on_click=state.date.set(getDateTime("date")))
Button("Save Time", on_click=state.time.set(getDateTime("time")))

Complete Example

from dars.all import *

app = App("Timestamp Demo")
state = State("state", last_action="", timestamp="")

form_data = collect_form(
    action=V("#action"),
    timestamp=getDateTime("locale")
)

@route("/")
def index():
    return Page(
        Container(
            Input(id="action", placeholder="What did you do?"),

            Button(
                "Record Action",
                on_click=form_data.to_state(state.last_action)
            ),

            Text("Last Action:", style="font-bold"),
            Text(text=useDynamic("state.last_action")),

            Button(
                "Update Timestamp",
                on_click=state.timestamp.set(getDateTime("locale"))
            ),

            Text("Current Time:", style="font-bold"),
            Text(text=useDynamic("state.timestamp"))
        )
    )

app.add_page("index", index())

if __name__ == "__main__":
    app.rTimeCompile()

Best Practices

Do: - Use useDynamic for simple text/value updates. - Use useWatch for side effects like logging, analytics, or complex logic. - Use useValue with selectors for form inputs that need value extraction. - Use consistent state naming (e.g., "user" , "cart" ). - Always use .int() or .float() before arithmetic operations with V() .

Don't: - Use with non-existent state paths. - Nest state paths more than 2 levels deep (currently supports stateName.property ). - Use arithmetic operators without numeric transformations.


Operations in Dars

Dars introduces a powerful, declarative system for mathematical expressions using operator overloading. Write complex calculations in pure Python without any inline JavaScript!

Overview

The V() helper supports: - Operator overloading - Use Python operators ( + , - , * , / , % , ** ) - Dynamic operators - Operators from Select/Input elements - Automatic precedence - Parentheses handled automatically - NaN validation - Safe handling of empty/invalid inputs - Type safety - Numeric operations require .float() or .int()


Basic Operations

String Concatenation

String concatenation works without any transformations:

from dars.all import *

# Simple concatenation
state.fullname.set(V(".first") + " " + V(".last"))

# With literals
state.message.set("Hello, " + V(".username") + "!")

# Multiple values
state.info.set(V(".name") + " - " + V(".email") + " - " + V(".phone"))

Arithmetic Operations

IMPORTANT : Arithmetic operations require .float() or .int() transformations:

# Addition
state.total.set(V(".price").float() + V(".tax").float())

# Subtraction
state.change.set(V(".paid").float() - V(".total").float())

# Multiplication
state.total.set(V(".price").float() * V(".quantity").int())

# Division
state.average.set(V(".sum").float() / V(".count").int())

# Module
state.remainder.set(V(".number").int() % V(".divisor").int())

# Power
state.result.set(V(".base").float() ** V(".exponent").int())

Why transformations are required: - Prevents accidental string concatenation (e.g., "5" * "3" = "555" in JavaScript) - Ensures type safety - Makes intent explicit


Complex Expressions

Operator Precedence

Dars automatically handles operator precedence following standard mathematical rules:

# a + b * c  →  a + (b * c)  ✅ Automatic
state.result.set(
    V(".a").float() + V(".b").float() * V(".c").float()
)

# (a + b) * c  →  (a + b) * c  ✅ Preserved
state.result.set(
    (V(".a").float() + V(".b").float()) * V(".c").float()
)

# a ** b ** c  →  a ** (b ** c)  ✅ Right-associative
state.result.set(
    V(".a").float() ** V(".b").float() ** V(".c").float()
)

Precedence Table:

Operator Precedence Associativity
** 3 (highest) Right
* , / , % 2 Left
+ , - 1 (lowest) Left

Nested Expressions

You can nest expressions infinitely:

# Complex calculation
state.result.set(
    ((V(".a").float() + V(".b").float()) * V(".c").float()) / 
    (V(".d").float() - V(".e").float())
)

# With mixed literals
state.total.set(
    (V(".price").float() * V(".qty").int()) * 1.15  # Add 15% tax
)

# Combining multiple operations
state.score.set(
    (V(".math").int() + V(".science").int() + V(".english").int()) / 3
)

Dynamic Operators

Use operators from Select or Input elements!

The .operator() Method

Mark a ValueRef as a dynamic operator using .operator() :

# Select with operator options
Select(
    class_name="operation",
    options=[
        SelectOption("+", "Add"),
        SelectOption("-", "Subtract"),
        SelectOption("*", "Multiply"),
        SelectOption("/", "Divide")
    ]
)

# Use in calculation
Button(
    "Calculate",
    on_click=calc.result.set(
        V(".num1").float() + V(".operation").operator() + V(".num2").float()
    )
)

How It Works

  1. Operator Extraction : V(".operation").operator() extracts the operator value
  2. Validation : Checks if operator is valid ( + , - , * , / , % , ** )
  3. Evaluation : Uses a switch statement to perform the operation
  4. Fallback : Defaults to + if operator is invalid

Complete Calculator Example

from dars.all import *

app = App("Calculator")

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", placeholder="First number"),
        Input(class_name="num2", input_type="number", placeholder="Second number"),

        # Calculate button - DECLARATIVE!
        Button(
            "Calculate",
            on_click=calc.result.set(
                V(".num1").float() + V(".operation").operator() + V(".num2").float()
            )
        ),

        # Result display
        Text(text=useDynamic("calc.result"))
    )

app.add_page("index", index())

if __name__ == "__main__":
    app.rTimeCompile()

Supported Operators

The .operator() method validates against this whitelist:

  • + - Addition
  • - - Subtraction
  • * - Multiplication
  • / - Division
  • % - Modulo
  • ** - Power

Invalid operators automatically fall back to + with a console warning.


NaN Validation

New in v1.6.4 : Automatic NaN handling prevents errors from empty or invalid inputs.

Automatic Validation

All mathematical expressions include built-in NaN validation:

# If inputs are empty or invalid
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

  1. Input Validation : Checks operands before calculation
  2. Result Validation : Checks result after calculation
  3. Console Logging : Warns when NaN is detected

Example Scenarios

# Empty inputs
V(".empty-input").float()  # → 0 (with warning)

# Invalid division
V(".num").float() / 0  # → 0 (with warning)

# Invalid operation
V(".text").float()  # → 0 (with warning)

State Integration

Using with State.set()

MathExpression objects work seamlessly with State.set() :

# Simple expression
calc.result.set(V(".a").float() + V(".b").float())

# Complex expression
calc.total.set(
    (V(".price").float() * V(".qty").int()) * 1.15
)

# With dynamic operator
calc.result.set(
    V(".num1").float() + V(".op").operator() + V(".num2").float()
)

Async Evaluation

All expressions are automatically wrapped in async IIFEs:

# Your code
calc.result.set(V(".a").float() + V(".b").float())

# Generated JavaScript
(async () => {
    const left = await (/* V(".a").float() */);
    const right = await (/* V(".b").float() */);

    if (isNaN(left) || isNaN(right)) {
        console.warn('[Dars] Invalid input: one or more values are NaN. Returning 0.');
        return 0;
    }

    const result = left + right;

    if (isNaN(result)) {
        console.warn('[Dars] Operation resulted in NaN. Returning 0.');
        return 0;
    }

    return result;
})()

Advanced Examples

Multi-Step Calculation

# Calculate total with tax and discount
Button(
    "Calculate Total",
    on_click=cart.total.set(
        ((V(".price").float() * V(".qty").int()) * 1.15) - V(".discount").float()
    )
)

Conditional Calculations

# Different calculations based on operator
Select(class_name="calc-type", options=[
    SelectOption("area", "Area"),
    SelectOption("perimeter", "Perimeter")
])

# Area: length * width
# Perimeter: 2 * (length + width)
Button(
    "Calculate",
    on_click=dScript(f"""
        const type = {V(".calc-type").get_code()};
        const length = {V(".length").float().get_code()};
        const width = {V(".width").float().get_code()};

        let result;
        if (type === 'area') {{
            result = length * width;
        }} else {{
            result = 2 * (length + width);
        }}

        window.Dars.change({{
            id: 'calc',
            dynamic: true,
            result: result
        }});
    """)
)

Scientific Calculator

# Power calculation
calc.result.set(V(".base").float() ** V(".exponent").float())

# Modulo for remainders
calc.remainder.set(V(".dividend").int() % V(".divisor").int())

# Complex formula: (a² + b²)^0.5 (Pythagorean theorem)
calc.hypotenuse.set(
    (V(".a").float() ** 2 + V(".b").float() ** 2) ** 0.5
)

Best Practices

Do

  1. Always use transformations for math

    V(".price").float() * V(".qty").int()  # ✅ Correct
    
  2. Use descriptive class names

    Input(class_name="price-input")  # ✅ Clear
    V(".price-input").float()
    
  3. Leverage automatic precedence

    V(".a").float() + V(".b").float() * V(".c").float()  # ✅ Auto-parens
    
  4. Use dynamic operators for flexibility

    V(".num1").float() + V(".op").operator() + V(".num2").float()  # ✅ Dynamic
    

Don't

  1. Don't use arithmetic without transformations

    V(".price") * V(".qty")  # ❌ TypeError
    
  2. Don't rely on implicit type coercion

    V(".number") + 5  # ❌ String concatenation, not addition
    
  3. Don't forget NaN is handled automatically

    # No need for manual checks
    if (isNaN(V(".num").float())) { ... }
    
    # Automatic validation
    calc.result.set(V(".num").float() + 10)
    

Type Reference

ValueRef

Created by V(selector) :

class ValueRef:
    def int() -> ValueRef          # Convert to integer
    def float() -> ValueRef        # Convert to float
    def upper() -> ValueRef        # Convert to uppercase
    def lower() -> ValueRef        # Convert to lowercase
    def trim() -> ValueRef         # Remove whitespace
    def operator() -> DynamicOperator  # Mark as dynamic operator

    # Operators
    def __add__(other) -> MathExpression
    def __sub__(other) -> MathExpression
    def __mul__(other) -> MathExpression
    def __truediv__(other) -> MathExpression
    def __mod__(other) -> MathExpression
    def __pow__(other) -> MathExpression

MathExpression

Created by operator overloading:

class MathExpression:
    # Supports all arithmetic operators
    # Automatically handles precedence
    # Validates NaN
    # Generates async JavaScript

DynamicOperator

Created by .operator() :

class DynamicOperator:
    VALID_OPERATORS = ['+', '-', '*', '/', '%', '**']
    # Validates operator at runtime
    # Falls back to '+' if invalid

Backend HTTP Utilities

Dars Framework provides a powerful, Pythonic system for handling HTTP requests and API communication without writing any JavaScript. The dars.backend module enables you to fetch data, bind it to components, and create reactive UIs entirely in Python.

Table of Contents


Quick Start with HTTP UTILS

from dars.all import *
from dars.backend import get, useData

app = App(title="API Demo")

# Create display component
user_display = Text("No data", id="user-name")
user_state = State(user_display, text="No data")

# Fetch and bind data - pure Python!
fetch_btn = Button(
    "Fetch User",
    on_click=get(
        id="userData",
        url="https://api.example.com/user/1",
        callback=user_state.text.set(useData('userData').name)
    )
)

app.set_root(Container(user_display, fetch_btn))

if __name__ == "__main__":
    app.rTimeCompile()

HTTP Functions

The dars.backend module provides standard HTTP methods that return dScript objects:

get(id, url, **options)

Performs a GET request.

from dars.backend import get

# Basic GET
get_user = get(
    id="userData",
    url="https://jsonplaceholder.typicode.com/users/1"
)

# With callback
get_user = get(
    id="userData",
    url="https://api.example.com/user/1",
    callback=status_state.text.set("✅ Loaded!"),
    on_error=status_state.text.set("❌ Error!")
)

post(id, url, body, **options)

Performs a POST request.

from dars.backend import post

# POST with JSON body
create_user = post(
    id="createResult",
    url="https://api.example.com/users",
    body={"name": "John", "email": "john@example.com"},
    callback=status_state.text.set("User created!")
)

Other Methods

  • put(id, url, body, **options) - Update resource
  • delete(id, url, **options) - Delete resource
  • patch(id, url, body, **options) - Partial update
  • fetch(id, url, method, **options) - Generic fetch

Common Options

All HTTP functions accept these options:

Option Type Description
id str Required . Operation ID (NOT HTML ID) for accessing data
url str Required . API endpoint URL
headers dict Custom HTTP headers
callback dScript Executed on success
on_error dScript Executed on error
parse_json bool Auto-parse JSON response (default: True )
timeout int Request timeout in milliseconds

Data Binding with useData()

The useData() function provides Pythonic access to fetched data using dot notation:

Basic Usage

from dars.backend import useData

# Access fetched data by operation ID
user_data = useData('userData')

# Access nested properties with dot notation
user_name = useData('userData').name
user_email = useData('userData').email
user_address_city = useData('userData').address.city

How It Works

  1. Operation ID : When you call get(id="userData", ...) , the response is stored in window.userData
  2. DataAccessor : useData('userData') creates a DataAccessor object
  3. Dot Notation : .name uses __getattr__ to create window.userData?.name
  4. RawJS Generation : The .code property generates the JavaScript expression

Binding to StateV2

The most powerful feature is binding API data directly to component states:

from dars.all import *
from dars.backend import get, useData

# Create components and states
name_display = Text("", id="user-name")
email_display = Text("", id="user-email")

name_state = State(name_display, text="")
email_state = State(email_display, text="")

# Fetch and bind - pure Python!
fetch_button = Button(
    "Fetch User",
    on_click=get(
        id="userData",
        url="https://jsonplaceholder.typicode.com/users/1",
        # Chain multiple state updates with .then()
        callback=(
            name_state.text.set(useData('userData').name)
            .then(email_state.text.set(useData('userData').email))
        )
    )
)

Chaining with .then()

Chain multiple operations sequentially:

# Update multiple components
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!"))
)

JSON Utilities

Helper functions for working with JSON data:

stringify(data, pretty=False)

Convert data to JSON string:

from dars.backend import stringify, useData

# Stringify fetched data
display_state.text.set(stringify(useData('userData'), pretty=True))

# Stringify Python objects
json_str = stringify({"name": "John", "age": 30})

parse(json_string)

Parse JSON string:

from dars.backend import parse

# Parse JSON string
data = parse('{"name": "John"}')

get_value(obj, path, default=None)

Safely access nested values:

from dars.backend import get_value, useData

# Safe nested access with default
city = get_value(useData('userData'), 'address.city', default='Unknown')

Component Management

Create, update, and delete components dynamically at runtime:

createComp(target, root, position="append")

Create a new component in the DOM:

from dars.backend import createComp

# Create new component
new_text = Text("Hello!", id="new-item")
create_btn.on_click = createComp(
    target=new_text,
    root="container-id",
    position="append"  # or "prepend", "before:id", "after:id"
)

Tip: You can create a State() for a component before it exists using a string ID:

# Create state with string ID
item_state = State("new-item", text="Hello!")

# Create component later
create_btn.on_click = createComp(
    target=Text("Hello!", id="new-item"),
    root="container-id"
)

# State works immediately!
update_btn.on_click = item_state.text.set("Updated!")

updateComp(target, **props)

Update component properties:

from dars.backend import updateComp

# Update component
update_btn.on_click = updateComp(
    "my-component-id",
    text="Updated!",
    style={"color": "red"}
)

deleteComp(id)

Remove a component from the DOM:

from dars.backend import deleteComp

# Delete component
delete_btn.on_click = deleteComp("component-id")

Best Practices

  1. Use Unique Operation IDs : Each HTTP operation should have a unique id to avoid conflicts
  2. Chain Updates : Use .then() to chain multiple state updates sequentially
  3. Handle Errors : Always provide on_error callbacks for better UX
  4. Leverage useData() : Use dot notation for clean, readable data access
  5. Combine with StateV2 : Bind API data directly to component states for reactive UIs

API Reference Summary

HTTP Functions

  • get(id, url, **options) - GET request
  • post(id, url, body, **options) - POST request
  • put(id, url, body, **options) - PUT request
  • delete(id, url, **options) - DELETE request
  • patch(id, url, body, **options) - PATCH request
  • fetch(id, url, method, **options) - Generic fetch

Data Access

  • useData(operation_id) - Access fetched data with dot notation
  • stringify(data, pretty=False) - Convert to JSON string
  • parse(json_string) - Parse JSON
  • get_value(obj, path, default=None) - Safe nested access

Component Management

  • createComp(target, root, position) - Create component
  • updateComp(target, **props) - Update component
  • deleteComp(id) - Delete component

For more examples, see the test files in tst/proj/test_http_demo.py and tst/proj/test_http_utils.py .


SSR Backend Setup

Dars provides a built-in create_ssr_app helper to easily serve your Dars application with Server-Side Rendering (SSR) using FastAPI.

Project Structure

When you run dars init --type ssr , Dars creates a backend structure for you: * backend/api.py : Entry point for the FastAPI server (Default Port: 8000). * backend/apiConfig.py : Configuration helper for environment management.

Configuration ( apiConfig.py )

To switch between Development and Production modes, simply edit the MODE variable in backend/apiConfig.py :

class DarsEnv:
    # Set this to "production" when deploying
    MODE = "development" 

    DEV = "development"
    BUILD = "production"

    # ...
  • Development : Backend runs on localhost:8000 , Frontend on localhost:3000 . dars dev proxies requests.
  • Production : Backend serves everything.

Running the Backend

# Start the SSR Backend (Port 3000)
python backend/api.py

In a separate terminal, run the frontend dev server:

# Start Frontend Dev Server (Port 8000)
dars dev

Events in Dars

This is the documentation for the events in Dars.

Event Calling System

Custom components in Dars can have events associated with them. You can set an event on a custom component using the set_event method.

self.set_event(EventTypes.CLICK, dScript("console.log('click')"))

Available Event Types

To use the event types, you need to import them from dars.core.events :

from dars.core.events import EventTypes

Here are the different event types available:

  • Mouse Events:

    • CLICK = "click"
    • DOUBLE_CLICK = "dblclick"
    • MOUSE_DOWN = "mousedown"
    • MOUSE_UP = "mouseup"
    • MOUSE_ENTER = "mouseenter"
    • MOUSE_LEAVE = "mouseleave"
    • MOUSE_MOVE = "mousemove"
  • Keyboard Events:

    • KEY_DOWN = "keydown"
    • KEY_UP = "keyup"
    • KEY_PRESS = "keypress"
  • Form Events:

    • CHANGE = "change"
    • INPUT = "input"
    • SUBMIT = "submit"
    • FOCUS = "focus"
    • BLUR = "blur"
  • Load Events:

    • LOAD = "load"
    • ERROR = "error"
    • RESIZE = "resize"

New in v1.2.2: Event arrays and dynamic handlers

  • Any on_* attribute can now accept:
    • A single script (InlineScript, FileScript, dScript) or plain JS string
    • An array mixing any of the above (executed sequentially)

Example using Mod.set :

Mod.set("btn1", on_click=[st1.state(0), dScript(code="console.log('clicked')")])

Runtime behavior:

  • Only one dynamic listener per event is active at a time; subsequent Mod.set replaces the previous one.
  • Dynamic handlers run in capture phase and stop propagation for the same event.
  • Returning to the default state (index 0) removes any dynamic listeners from that element and restores its initial DOM.

Backend HTTP Integration

Dars provides HTTP utilities that can be used directly in event handlers:

from dars.all import *
from dars.backend import get, post, useData

# GET request on button click
fetch_btn = Button(
    "Fetch Data",
    on_click=get(
        id="apiData",
        url="https://api.example.com/data",
        callback=status_state.text.set("✅ Loaded!")
    )
)

# POST request with data binding
submit_btn = Button(
    "Submit",
    on_click=post(
        id="submitResult",
        url="https://api.example.com/submit",
        body={"name": "John", "email": "john@example.com"},
        callback=result_state.text.set(useData('submitResult').message)
    )
)

# Chain HTTP request with state updates
button.on_click = [
    status_state.text.set("Loading..."),
    get(
        id="userData",
        url="https://api.example.com/user/1",
        callback=(
            name_state.text.set(useData('userData').name)
            .then(status_state.text.set("Done!"))
        )
    )
]

Keyboard Events in Dars

Dars provides a powerful and intuitive system for handling keyboard events in your applications. This guide covers everything from basic key detection to advanced global shortcuts.


Basic Keyboard Events

All Dars components support the on_key_press event handler for keyboard interactions.

Simple Example

from dars.all import *

Input(
    id="search",
    on_key_press=log("Key pressed!")
)

Note: Use on_key_press as the universal keyboard event. The older on_key_down and on_key_up events have been deprecated in favor of this simpler approach.


KeyCode Constants

The KeyCode class provides constants for all keyboard keys, making your code more readable and maintainable.

Available Keys

from dars.all import *

# Navigation keys
KeyCode.ENTER
KeyCode.TAB
KeyCode.ESCAPE  # or KeyCode.ESC
KeyCode.BACKSPACE
KeyCode.DELETE

# Arrow keys
KeyCode.UP       # or KeyCode.ARROWUP
KeyCode.DOWN     # or KeyCode.ARROWDOWN
KeyCode.LEFT     # or KeyCode.ARROWLEFT
KeyCode.RIGHT    # or KeyCode.ARROWRIGHT

# Letters (a-z)
KeyCode.A
KeyCode.B
# ... through ...
KeyCode.Z

# Numbers
KeyCode.ZERO  # or KeyCode.0
KeyCode.ONE   # or KeyCode.1
# ... through ...
KeyCode.NINE  # or KeyCode.9

# Function keys
KeyCode.F1
KeyCode.F2
# ... through ...
KeyCode.F12

# Special characters
KeyCode.SPACE
KeyCode.PLUS
KeyCode.MINUS
KeyCode.SLASH
KeyCode.COMMA
KeyCode.PERIOD

Dynamic Key Access

# Get key code by name
key = KeyCode.key('enter')  # Returns 'Enter'
key = KeyCode.key('A')      # Returns 'a'

The onKey() Helper

The onKey() function is the recommended way to handle specific keyboard keys with optional modifier keys.

Basic Usage

from dars.all import *

# Simple key detection
Input(
    on_key_press=onKey(KeyCode.ENTER, log("Enter pressed!"))
)

With Modifiers

# Ctrl modifier
Container(
    on_key_press=onKey(KeyCode.S, alert("Saving..."), ctrl=True)
)

# Multiple modifiers
Container(
    on_key_press=onKey(KeyCode.Z, log("Redo"), ctrl=True, shift=True)
)

Available Modifiers

  • ctrl - Ctrl key (Command on Mac)
  • shift - Shift key
  • alt - Alt key
  • meta - Meta/Command key

Complete Example

from dars.all import *

app = App("onKey Example")

formState = State("form", message="")

@route("/")
def index():
    return Page(
        Input(
            id="input",
            placeholder="Press Enter to submit",
            on_key_press=onKey(
                KeyCode.ENTER,
                formState.message.set("Submitted!"),
                ctrl=False  # Just Enter, no modifier needed
            )
        ),
        Text(useDynamic("form.message"))
    )

app.add_page("index", index(), index=True)

The switch() Function

Use switch() to handle multiple different keys in a single event handler.

Basic Usage

from dars.all import *

Input(
    on_key_press=switch({
        KeyCode.ENTER: log("Enter pressed"),
        KeyCode.ESCAPE: alert("Escape pressed"),
    })
)

With Multiple Actions

formState = State("form", username="", password="")

Input(
    on_key_press=switch({
        KeyCode.ENTER: [
            formState.message.set("Submitted!"),
            alert("Form submitted!")
        ],
        KeyCode.ESCAPE: [
            clearInput("username"),
            clearInput("password"),
            formState.message.set("Cleared!")
        ]
    })
)

Complete Example

from dars.all import *

app = App("KeyCode Clean Example")

# State
formState = State("form", 
    username="", 
    password="", 
    message="Use keyboard shortcuts!"
)

@FunctionComponent
def LoginForm(**props):
    return f'''
    <div {Props.id} {Props.class_name} {Props.style}>
        <h2>Login Form with Keyboard Shortcuts</h2>
        <p style="color: #666;">{useDynamic("form.message")}</p>

        <input 
            type="text" 
            id="username-input"
            placeholder="Username"
            style="display: block; margin: 10px 0; padding: 8px; width: 300px;"
        />

        <input 
            type="password" 
            id="password-input"
            placeholder="Password"
            style="display: block; margin: 10px 0; padding: 8px; width: 300px;"
        />

        <div style="margin-top: 30px; padding: 15px; background: #f5f5f5; border-radius: 4px;">
            <h3 style="margin-top: 0;">Global Keyboard Shortcuts:</h3>
            <ul style="margin: 0; padding-left: 20px;">
                <li><strong>Ctrl+Enter</strong> - Submit form (shows alert)</li>
                <li><strong>Ctrl+F</strong> - Clear form</li>
                <li><strong>Ctrl+S</strong> - Save document</li>
            </ul>
            <p style="margin-top: 10px; font-size: 0.9em; color: #666;">
                Note: These are GLOBAL shortcuts that work anywhere on the page without blocking normal typing.
            </p>
        </div>
    </div>
    '''

@route("/")
def index():
    return Page(
        LoginForm(id="login-form"),
        Input(value="", on_key_up=onKey("R", action=log("Logged"))),
        # Buttons using State.set() and utils_ds functions
        Button(
            "Submit",
            on_click=[
                formState.message.set("Form submitted via button!"),
                alert("Form submitted!")
            ]
        ),

        Button(
            "Clear", 
            on_click=[
                formState.username.set(""),
                formState.password.set(""),
                formState.message.set("Form cleared via button!"),
                clearInput("username-input"),
                clearInput("password-input")
            ]
        ),

        Button(
            "Show Username",
            on_click=alert(V("#username-input"))
        ),

        Button(
            "Log Message",
            on_click=log(V("form.message"))
        ),
    )

addGlobalKeys(app, {
    (KeyCode.ENTER, 'ctrl'): [
        formState.message.set("Form submitted with Ctrl+Enter!"),
        alert("Form submitted!")
    ],
    (KeyCode.F, 'ctrl'): [
        formState.username.set(""),
        formState.password.set(""),
        formState.message.set("Form cleared with Ctrl+F!"),
        clearInput("username-input"),
        clearInput("password-input")
    ],
    (KeyCode.S, 'ctrl'): alert("Document saved! (Ctrl+S)"),
})

app.add_page("index", index(), title="KeyCode Example", index=True)

if __name__ == "__main__":
    app.rTimeCompile()

Global Keyboard Shortcuts

Use addGlobalKeys() to create app-wide keyboard shortcuts that work anywhere on the page.

Why Global Shortcuts?

Global shortcuts are perfect for: - App-level commands (Save, Undo, Redo) - Navigation shortcuts - Quick actions that should work anywhere

Important: Always use modifier keys (Ctrl, Alt, etc.) with global shortcuts to avoid blocking normal typing in input fields.

Basic Usage

from dars.all import *

app = App("Global Shortcuts")

# Define your actions
def save_document():
    return alert("Document saved!")

def undo():
    return log("Undo action")

# Add global shortcuts
addGlobalKeys(app, {
    (KeyCode.S, 'ctrl'): save_document(),
    (KeyCode.Z, 'ctrl'): undo()
})

With Multiple Actions

formState = State("form", data="")

addGlobalKeys(app, {
    (KeyCode.ENTER, 'ctrl'): [
        formState.data.set("Submitted!"),
        alert("Form submitted with Ctrl+Enter")
    ],
    (KeyCode.ESCAPE, 'ctrl'): [
        formState.data.set(""),
        log("Form cleared")
    ]
})

Multiple Modifiers

addGlobalKeys(app, {
    (KeyCode.Z, 'ctrl'): undo(),
    (KeyCode.Z, 'ctrl', 'shift'): redo(),
    (KeyCode.S, 'ctrl', 'shift'): save_as()
})

Complete Example

from dars.all import *

app = App("Global Shortcuts Example")

docState = State("document", 
    content="", 
    saved=False,
    message="Ready"
)

@route("/")
def index():
    return Page(
        Container(
            Text("Document Editor", style={"font-size": "24px", "font-weight": "bold"}),
            Text(useDynamic("document.message"), style={"color": "#666"}),

            Input(
                id="editor",
                placeholder="Start typing...",
                style={"width": "100%", "min-height": "200px"}
            ),

            Container(
                style={"margin-top": "20px", "padding": "15px", "background": "#f5f5f5"},
                children=[
                    Text("Global Shortcuts:", style={"font-weight": "bold"}),
                    Text("• Ctrl+S - Save"),
                    Text("• Ctrl+Z - Undo"),
                    Text("• Ctrl+Shift+Z - Redo"),
                ]
            )
        )
    )

# Global keyboard shortcuts
addGlobalKeys(app, {
    (KeyCode.S, 'ctrl'): [
        docState.saved.set(True),
        docState.message.set("Document saved!"),
        alert("Saved!")
    ],
    (KeyCode.Z, 'ctrl'): [
        docState.message.set("Undo"),
        log("Undo action")
    ]
})

app.add_page("index", index(), index=True)

if __name__ == "__main__":
    app.rTimeCompile()

Best Practices

1. Use Modifiers for Global Shortcuts

Bad - Blocks typing:

addGlobalKeys(app, {
    KeyCode.ENTER: submit_form()  # Blocks Enter in all inputs!
})

Good - Doesn't interfere:

addGlobalKeys(app, {
    (KeyCode.ENTER, 'ctrl'): submit_form()  # Only Ctrl+Enter
})

2. Use onKey() for Component-Specific Keys

For specific components:

Input(
    id="search",
    on_key_press=onKey(KeyCode.ENTER, perform_search())
)

For app-wide shortcuts:

addGlobalKeys(app, {
    (KeyCode.F, 'ctrl'): focus("search")
})

3. Use switch() for Multiple Keys

Bad - Repetitive:

Input(
    on_key_press=onKey(KeyCode.ENTER, action1())
)
Input(
    on_key_press=onKey(KeyCode.ESCAPE, action2())
)

Good - Clean:

Container(
    on_key_press=switch({
        KeyCode.ENTER: action1(),
        KeyCode.ESCAPE: action2()
    })
)

4. Combine with State and V()

formState = State("form", username="")

Input(
    id="username",
    on_key_press=onKey(KeyCode.ENTER, formState.username.set(V("#username")))
)

Summary

  • Use on_key_press for all keyboard events (replaces on_key_down and on_key_up )
  • Use KeyCode constants for readable key references
  • Use onKey() for single key detection with optional modifiers
  • Use switch() for handling multiple different keys
  • Use addGlobalKeys() for app-wide shortcuts (always with modifiers!)
  • Combine with State and V() for dynamic, reactive keyboard interactions

Dars - Exporter Documentation

Introduction

Exporters are the heart of Dars that allow transforming applications written in Python to different technologies and platforms. Each exporter translates Dars components, styles, and scripts to the native code of the target platform.

Exporter Architecture

Base Exporter Class

All exporters inherit from the base Exporter class:

from abc import ABC, abstractmethod

class Exporter(ABC):
    def __init__(self):
        self.templates_path = "templates/"

    @abstractmethod
    def export(self, app: App, output_path: str) -> bool:
        """Exports the application to the specific format"""
        pass

    @abstractmethod
    def render_component(self, component: Component) -> str:
        """Renders an individual component"""
        pass

    @abstractmethod
    def get_platform(self) -> str:
        """Returns the name of the platform"""
        pass

Exportation Flow

  1. Validation : Verify that the application is valid
  2. Preparation : Create directory structure
  3. Rendering : Convert components to the target format
  4. Generation : Create configuration and dependency files
  5. Finalization : Write files to the system

Web Exporters

HTML/CSS/JavaScript

The HTML exporter generates standard web applications that can run in any browser.

Features

  • Compatibility : Works in all modern browsers
  • Simplicity : No requires build tools
  • Performance : Fast loading and efficient execution
  • SEO : Content indexable by search engines

Usage

dars export my_app.py --format html --output ./dist

Generated Structure

dist/
├── index.html      # Main page
├── styles.css      # CSS styles
└── script.js       # JavaScript logic

Example Output

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Application</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div id="container_123" class="dars-container" style="display: flex; flex-direction: column; padding: 20px;">
        <span id="text_456" class="dars-text" style="font-size: 24px; color: #333;">Hello Dars!</span>
        <button id="button_789" class="dars-button" style="background-color: #007bff; color: white;">Click</button>
    </div>
    <script src="script.js"></script>
</body>
</html>

styles.css

/* Base Dars styles */
* {
    box-sizing: border-box;
}

body {
    margin: 0;
    padding: 0;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
}

.dars-button {
    display: inline-block;
    padding: 8px 16px;
    border: 1px solid #ccc;
    background-color: #f8f9fa;
    color: #333;
    cursor: pointer;
    border-radius: 4px;
    font-size: 14px;
}

.dars-button:hover {
    background-color: #e9ecef;
}

Advantages

  • Universality : Works in any web server
  • Debugging : Easy to debug with browser tools
  • Personalization : CSS and JavaScript completely modifiable
  • Hosting : Can be hosted on any static hosting service

Use Cases

  • Corporate websites
  • Landing pages
  • Simple web applications
  • Quick prototypes
  • Interactive documentation

Exporter Personalization

Extending Existing Exporters

from dars.exporters.base import Exporter

class MyCustomExporter(Exporter):
    def get_platform(self):
        return "my_custom_platform"

    def export(self, app, output_path):
        # Implement custom export logic
        return True

    def render_component(self, component):
        # Implement custom component rendering
        return "generated_code"

Desktop Export (BETA)

The desktop exporter allows you to package your Dars app as a native desktop application. This feature is currently in BETA: usable for testing and internal tooling, but not recommended for production deployments yet.

Status and Scope

  • BETA: Many options and integrations are still evolving (advanced packaging, signing, etc.).
  • Cross‑platform targets supported: Windows, Linux, macOS (host restrictions apply for macOS signing, and linux if you don't have docker).
  • Underlying tech: Electron is used to deliver a native desktop container for your web UI.
  • Hot Reload: dars dev with desktop apps is fully supported, but for now it closes and reopen the electron dev app when a file change is detected.

    Quickstart desktop Export

  1. Initialize or update your project for desktop:

    dars init --type desktop
    # or if you already have a project
    dars init --update
    
  2. Ensure your project config has desktop format:

    {
      "entry": "main.py",
      "format": "desktop",
      "outdir": "dist",
      "targetPlatform": "auto"
    }
    
  3. Verify optional tooling (Node/Bun, Electron, electron‑builder):

    dars doctor --all --yes
    
  4. Build your desktop app:

    dars build
    

Artifacts will be placed in dist/ . The desktop source (used for packaging) is emitted to dist/source-electron/ .

Native Functions & Dynamic Updates

You can access native filesystem functions via the dars.desktop module:

from dars.desktop import read_text, write_text, read_file, write_file, list_directory, get_value
from dars.core.state import this
from dars.scripts.dscript import RawJS, dScript

File System Operations

The desktop module provides async file operations that return dScript objects, perfect for chaining with this().state() :

Reading Text Files

# Simple read - button updates itself with file content
read_btn = Button("Load Config",
    on_click=read_text("config.txt").then(
        this().state(text=RawJS(dScript.ARG))
    )
)

# Update another component
display = Text("", id="display")
load_btn = Button("Load Data",
    on_click=read_text("data.txt").then(
        update_component("display", text=RawJS(dScript.ARG))
    )
)

Writing Text Files

save_btn = Button("Save",
    on_click=write_text("output.txt", "Hello Dars!").then(
        this().state(text="Saved!", style={"color": "green"})
    )
)

Listing Directories

from dars.desktop import list_directory, get_value


Button("Browse",
    on_click=list_directory(".").then(
        this().state(id="file-list", html=RawJS("""
            value.map(f => {
                const icon = f.isDirectory ? '📁' : '📄';
                return `<div>${icon} ${f.name}</div>`;
            }).join('')
        """))
    )
)


Button("Python Files",
    on_click=list_directory(".", "*.py").then(
        this().state(id="count", text=RawJS("`Found ${value.length} files`"))
    )
)


Input(id="path", value=".")
Button("List Directory",
    on_click=list_directory(get_value("path")).then(
        this().state(id="output", html=RawJS("value.map(f => f.name).join('<br>')"))
    )
)


list_directory(".", "*", include_size=True)

Binary File Operations

img_data = read_file("image.png")


write_file("output.bin", data_bytes)

Chaining Multiple Operations

Use dScript.then() for sequential operations:

# Read → Process → Update → Write
Button("Process File",
    on_click=read_text("input.txt")
        .then(dScript(code="const processed = value.toUpperCase(); return processed;"))
        .then(write_text("output.txt", RawJS("processed")))
        .then(this().state(text="Complete!"))
)

All file paths are relative to the app's directory. See State Management and Scripts for more details on this() and chaining.

Platform Targeting

  • targetPlatform : auto | windows | linux | macos .
  • On non‑mac hosts, building for macOS is not supported.

Metadata and Packaging Notes

  • App metadata is taken from your App instance when available: title, description, author, version.
  • If version is not set, a default 0.1.0 is used and a warning is shown.
  • Package manager and Electron version are pinned by the exporter to make builds predictable.

Caveats (BETA)

  • Not yet recommended for production.
  • Some advanced packaging and signing options may require manual configuration.
  • Expect changes to configuration keys and defaults as the feature matures.

Dars - Script System

Introduction to Scripts

The script system of Dars allows adding interactive logic and dynamic behaviors to applications. Scripts are written in JavaScript and seamlessly integrate with UI components.

Fundamentals of Scripts

What are Scripts?

Scripts in Dars are fragments of JavaScript code that:

  • Handle user interface events
  • Implement client-side business logic
  • Provide advanced interactivity
  • Run in the context of the exported application

Types of Scripts

Dars supports three main types of scripts:

  1. InlineScript : Code defined directly in Python
  2. FileScript : Code loaded from external files
  3. dScript : Flexible script that can be defined either inline (as a string) or as a reference to an external file. Only one mode is allowed at a time.

Base Script Class

All scripts inherit from the base Script class:

from abc import ABC, abstractmethod

class Script(ABC):
    def __init__(self):
        pass

    @abstractmethod
    def get_code(self) -> str:
        """Retorna el código del script"""
        pass

dScript

When to use dScript

dScript is a flexible class that allows you to define a script as either: - Inline JavaScript (via the code argument) - Or as a reference to an external file (via the file_path argument)

But never both at the same time . This is useful for presets, user-editable actions, and advanced integrations.

Basic Syntax

from dars.scripts.dscript import dScript

# Inline JS
script_inline = dScript(code="""
function hello() { alert('Hello from dScript!'); }
document.addEventListener('DOMContentLoaded', hello);
""")

# External file
script_file = dScript(file_path="./scripts/my_script.js")

Example: Editable JS preset from Python

from dars.scripts.dscript import dScript

custom_action = dScript(code="""
function customClick() {
    alert('Custom action from preset!');
}
document.addEventListener('DOMContentLoaded', function() {
    var btn = document.getElementById('my-btn');
    if (btn) btn.onclick = customClick;
});
""")

app.add_script(custom_action)

Chaining Scripts ( .then() )

You can chain multiple dScript objects using the .then() method. This is particularly useful when working with asynchronous operations like file reading in Electron.

from dars.desktop import read_text
from dars.core.state import this
from dars.scripts.dscript import RawJS, dScript

# Read a file and update a component with its content
# The result of the previous script is available as dScript.ARG (which resolves to 'value')
read_op = read_text("data.txt")
update_op = this().state(text=RawJS(dScript.ARG))

chained_script = read_op.then(update_op)

The RawJS wrapper ensures that dScript.ARG is treated as a variable name ( value ) rather than a string literal "value" .

State Navigation with state.state()

The state.state(idx) method allows you to navigate a component to a specific state index. This is the recommended way to trigger state transitions.

Requirements : - The component must have a dState defined with the target index in its states array - The index must be valid (0 to states.length - 1)

Example: Toggle Button

from dars.all import *
from dars.core.state import dState

# Create a button that toggles between two states
toggle_btn = Button("Off", id="ToggleBtn")

# Define states for the button
toggle_state = dState("toggle", component=toggle_btn, states=[0, 1])

# Configure state 1 appearance and behavior
toggle_state.cState(1, mods=[
    Mod.set(toggle_btn, 
        text="On",
        style={'background-color': 'green', 'color': 'white'},
        on_click=toggle_state.state(0)  # Return to state 0 when clicked
    )
])

# Initial click navigates to state 1
toggle_btn.on_click = toggle_state.state(1)

Example: Cycle Through States

# Create a button that cycles through multiple states
cycle_btn = Button("State 0", id="CycleBtn")
cycle_state = dState("cycler", component=cycle_btn, states=[0, 1, 2, 3])

# Each state navigates to the next
cycle_state.cState(1, mods=[
    Mod.set(cycle_btn, text="State 1", on_click=cycle_state.state(2))
])
cycle_state.cState(2, mods=[
    Mod.set(cycle_btn, text="State 2", on_click=cycle_state.state(3))
])
cycle_state.cState(3, mods=[
    Mod.set(cycle_btn, text="State 3", on_click=cycle_state.state(0))
])

cycle_btn.on_click = cycle_state.state(1)  # Start the cycle

InlineScript

Basic Syntax InlineScript

from dars.scripts.script import InlineScript

script = InlineScript("""
function saludar() {
    alert('¡Hola desde Dars!');
}

document.addEventListener('DOMContentLoaded', function() {
    console.log('Aplicación cargada');
});
""")

Integration with Exporter

The exporter ( html_css_js.py ) automatically detects and exports all scripts of type dScript , InlineScript , and FileScript . You can safely mix and match them in your app, and all will be included in the generated JS.

  • Script objects embedded in state bootstrap (e.g., inside Mod.set(..., on_*=...) ) are serialized to a JSON-safe form as { "code": "..." } and reconstituted at runtime.
  • Event attributes ( on_* ) accept a single script or an array of scripts (any mix of InlineScript, FileScript, dScript, or raw JS strings). The runtime runs them sequentially and guarantees a single active dynamic listener per event.

FileScript

Basic Syntax for FileScript

from dars.scripts.script import FileScript

# Load script from file
script = FileScript("./scripts/mi_script.js")

Utility Scripts ( utils_ds )

Dars provides a collection of utility functions in dars.scripts.utils_ds (exported in dars.all ) that return pre-configured dScript objects for common tasks. These allow you to implement interactivity without writing raw JavaScript.

  • goTo(href) : Navigate to a URL in the current tab.
  • goToNew(href) : Open a URL in a new tab.
  • reload() : Reload the current page.
  • goBack() : Navigate back in browser history.
  • goForward() : Navigate forward in browser history.
Button("Home", on_click=goTo("/"))
Button("Docs", on_click=goToNew("https://docs.dars.dev"))

DOM Manipulation

  • show(id) : Show an element (display: block).
  • hide(id) : Hide an element (display: none).
  • toggle(id) : Toggle visibility.
  • setText(id, text) : Set text content.
  • addClass(id, class_name) : Add a CSS class.
  • removeClass(id, class_name) : Remove a CSS class.
  • toggleClass(id, class_name) : Toggle a CSS class.
Button("Show Details", on_click=show("details-panel"))
Button("Toggle Theme", on_click=toggleClass("app-root", "dark-mode"))

Timeouts

  • setTimeout(delay, code) : Set a timeout to execute a script after a delay.
Button("Delayed Action", on_click=setTimeout(delay=2000, code="alert('Delayed!')"))

Modals

  • showModal(id) : Show a Dars Modal component (handles hidden attribute and class).
  • hideModal(id) : Hide a Dars Modal component.
Button("Open Modal", on_click=showModal("my-modal"))

Forms

  • submitForm(form_id) : Submit a form.
  • resetForm(form_id) : Reset a form.
  • getValue(input_id, target_id) : Copy value from input to another element's text.
  • clearInput(input_id) : Clear an input field.
Button("Submit", on_click=submitForm("contact-form"))
Button("Clear", on_click=clearInput("search-box"))

Storage (localStorage)

  • saveToLocal(key, value) : Save string value.
  • loadFromLocal(key, target_id) : Load value and set as text of target element.
  • removeFromLocal(key) : Remove item.
  • clearLocalStorage() : Clear all storage.
Button("Save Prefs", on_click=saveToLocal("theme", "dark"))

Clipboard

  • copyToClipboard(text) : Copy text string.
  • copyElementText(id) : Copy text content of an element.
Button("Copy Code", on_click=copyElementText("code-block"))

Scroll

  • scrollTo(x, y) : Scroll to position.
  • scrollToTop() : Smooth scroll to top.
  • scrollToBottom() : Smooth scroll to bottom.
  • scrollToElement(id) : Smooth scroll to specific element.
Button("Back to Top", on_click=scrollToTop())

Alerts & Focus

  • alert(message) : Show browser alert.
  • confirm(message, on_ok, on_cancel) : Show confirm dialog.
read_op = read_text("data.txt")
update_op = this().state(text=RawJS(dScript.ARG))

chained_script = read_op.then(update_op)

The RawJS wrapper ensures that dScript.ARG is treated as a variable name ( value ) rather than a string literal "value" .

State Navigation with state.state()

The state.state(idx) method allows you to navigate a component to a specific state index. This is the recommended way to trigger state transitions.

Requirements : - The component must have a dState defined with the target index in its states array - The index must be valid (0 to states.length - 1)

Example: Toggle Button

from dars.all import *
from dars.core.state import dState

# Create a button that toggles between two states
toggle_btn = Button("Off", id="ToggleBtn")

# Define states for the button
toggle_state = dState("toggle", component=toggle_btn, states=[0, 1])

# Configure state 1 appearance and behavior
toggle_state.cState(1, mods=[
    Mod.set(toggle_btn, 
        text="On",
        style={'background-color': 'green', 'color': 'white'},
        on_click=toggle_state.state(0)  # Return to state 0 when clicked
    )
])

# Initial click navigates to state 1
toggle_btn.on_click = toggle_state.state(1)

Example: Cycle Through States

# Create a button that cycles through multiple states
cycle_btn = Button("State 0", id="CycleBtn")
cycle_state = dState("cycler", component=cycle_btn, states=[0, 1, 2, 3])

# Each state navigates to the next
cycle_state.cState(1, mods=[
    Mod.set(cycle_btn, text="State 1", on_click=cycle_state.state(2))
])
cycle_state.cState(2, mods=[
    Mod.set(cycle_btn, text="State 2", on_click=cycle_state.state(3))
])
cycle_state.cState(3, mods=[
    Mod.set(cycle_btn, text="State 3", on_click=cycle_state.state(0))
])

cycle_btn.on_click = cycle_state.state(1)  # Start the cycle

InlineScript

Basic Syntax InlineScript

from dars.scripts.script import InlineScript

script = InlineScript("""
function saludar() {
    alert('¡Hola desde Dars!');
}

document.addEventListener('DOMContentLoaded', function() {
    console.log('Aplicación cargada');
});
""")

Integration with Exporter

The exporter ( html_css_js.py ) automatically detects and exports all scripts of type dScript , InlineScript , and FileScript . You can safely mix and match them in your app, and all will be included in the generated JS.

  • Script objects embedded in state bootstrap (e.g., inside Mod.set(..., on_*=...) ) are serialized to a JSON-safe form as { "code": "..." } and reconstituted at runtime.
  • Event attributes ( on_* ) accept a single script or an array of scripts (any mix of InlineScript, FileScript, dScript, or raw JS strings). The runtime runs them sequentially and guarantees a single active dynamic listener per event.

FileScript

Basic Syntax for FileScript

from dars.scripts.script import FileScript

# Load script from file
script = FileScript("./scripts/mi_script.js")

Utility Scripts ( utils_ds )

Dars provides a collection of utility functions in dars.scripts.utils_ds (exported in dars.all ) that return pre-configured dScript objects for common tasks. These allow you to implement interactivity without writing raw JavaScript.

  • goTo(href) : Navigate to a URL in the current tab.
  • goToNew(href) : Open a URL in a new tab.
  • reload() : Reload the current page.
  • goBack() : Navigate back in browser history.
  • goForward() : Navigate forward in browser history.
Button("Home", on_click=goTo("/"))
Button("Docs", on_click=goToNew("https://docs.dars.dev"))

DOM Manipulation

  • show(id) : Show an element (display: block).
  • hide(id) : Hide an element (display: none).
  • toggle(id) : Toggle visibility.
  • setText(id, text) : Set text content.
  • addClass(id, class_name) : Add a CSS class.
  • removeClass(id, class_name) : Remove a CSS class.
  • toggleClass(id, class_name) : Toggle a CSS class.
Button("Show Details", on_click=show("details-panel"))
Button("Toggle Theme", on_click=toggleClass("app-root", "dark-mode"))

Timeouts

  • setTimeout(delay, code) : Set a timeout to execute a script after a delay.
Button("Delayed Action", on_click=setTimeout(delay=2000, code="alert('Delayed!')"))

Modals

  • showModal(id) : Show a Dars Modal component (handles hidden attribute and class).
  • hideModal(id) : Hide a Dars Modal component.
Button("Open Modal", on_click=showModal("my-modal"))

Forms

  • submitForm(form_id) : Submit a form.
  • resetForm(form_id) : Reset a form.
  • getValue(input_id, target_id) : Copy value from input to another element's text.
  • clearInput(input_id) : Clear an input field.
Button("Submit", on_click=submitForm("contact-form"))
Button("Clear", on_click=clearInput("search-box"))

Storage (localStorage)

  • saveToLocal(key, value) : Save string value.
  • loadFromLocal(key, target_id) : Load value and set as text of target element.
  • removeFromLocal(key) : Remove item.
  • clearLocalStorage() : Clear all storage.
Button("Save Prefs", on_click=saveToLocal("theme", "dark"))

Clipboard

  • copyToClipboard(text) : Copy text string.
  • copyElementText(id) : Copy text content of an element.
Button("Copy Code", on_click=copyElementText("code-block"))

Scroll

  • scrollTo(x, y) : Scroll to position.
  • scrollToTop() : Smooth scroll to top.
  • scrollToBottom() : Smooth scroll to bottom.
  • scrollToElement(id) : Smooth scroll to specific element.
Button("Back to Top", on_click=scrollToTop())

Alerts & Focus

  • alert(message) : Show browser alert.
  • confirm(message, on_ok, on_cancel) : Show confirm dialog.
  • log(message) : Log to console.
  • focus(id) : Focus an element.
  • blur(id) : Blur an element.
Button("Delete", on_click=confirm(
    "Are you sure?", 
    on_ok="console.log('Deleted')", 
    on_cancel="console.log('Cancelled')"
))

Animation System

Dars includes a comprehensive animation system with 15+ built-in animations. All animation functions return dScript objects and can be chained for complex sequences.

Basic Animations

fadeIn / fadeOut

from dars.all import fadeIn, fadeOut

# Fade in an element
button.on_click = fadeIn(id="box", duration=500, easing="ease")

# Fade out an element
button.on_click = fadeOut(id="box", duration=500, hide_after=True)

Parameters: - id : Element ID to animate - duration : Animation duration in milliseconds (default: 500) - easing : CSS easing function (default: "ease") - hide_after : For fadeOut, set display:none after animation (default: True)

slideIn / slideOut

from dars.all import slideIn, slideOut

# Slide in from left
button.on_click = slideIn(id="panel", direction="left", duration=400)

# Slide out to right
button.on_click = slideOut(id="panel", direction="right", duration=400)

Directions: "left" , "right" , "top" , "bottom" , "top-left" , "top-right" , "bottom-left" , "bottom-right"

scaleIn / scaleOut

from dars.all import scaleIn, scaleOut

# Scale in from 0.3 to 1
button.on_click = scaleIn(id="popup", from_scale=0.3, duration=400)

# Scale out to 0.5
button.on_click = scaleOut(id="popup", to_scale=0.5, duration=400)

Interactive Animations

shake

from dars.all import shake

# Shake effect
button.on_click = shake(id="alert", intensity=10, duration=500)

Parameters: - intensity : Shake distance in pixels (default: 10) - duration : Total animation duration (default: 500)

bounce

from dars.all import bounce

# Bounce effect
button.on_click = bounce(id="element", distance=20, duration=600)

pulse

from dars.all import pulse

# Pulse effect
button.on_click = pulse(id="button", scale=1.1, duration=400, iterations=2)

Parameters: - scale : Maximum scale factor (default: 1.1) - iterations : Number of pulses (default: 1, use "infinite" for continuous)

rotate

from dars.all import rotate

# Rotate 360 degrees
button.on_click = rotate(id="spinner", degrees=360, duration=1000)

flip

from dars.all import flip

# Flip on Y axis
button.on_click = flip(id="card", axis="y", duration=600)

Axis: "x" (horizontal), "y" (vertical)

Color and Size Animations

colorChange

from dars.all import colorChange

# Change background color
button.on_click = colorChange(
    id="box",
    property="background",
    from_color="#ff0000",
    to_color="#00ff00",
    duration=500
)

Properties: "color" , "background" , "border-color"

morphSize

from dars.all import morphSize

# Morph to new size
button.on_click = morphSize(
    id="box",
    to_width="300px",
    to_height="200px",
    duration=500
)

Animation Chaining

sequence

Chain multiple animations to run one after another:

from dars.all import sequence, fadeIn, pulse, shake

button.on_click = sequence(
    fadeIn(id="box", duration=300),
    pulse(id="box", scale=1.2, iterations=2),
    shake(id="box", intensity=5)
)

parallel

Run animations simultaneously (use .then() with same timing):

from dars.all import fadeIn, scaleIn

# Both animations run at the same time
button.on_click = fadeIn(id="box").then(scaleIn(id="other-box"))

Chaining with State Operations

Animations integrate seamlessly with State V2:

from dars.all import *

display = Text("0", id="counter")
counter = State(display, text=0)

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)
)

Complete Animation Example

from dars.all import *

app = App("Animation Demo")

# Create animated box
box = Container(
    Text("Animate Me!", style={"color": "white"}),
    id="anim-box",
    style={
        "background": "linear-gradient(135deg, #667eea, #764ba2)",
        "padding": "40px",
        "border-radius": "16px",
        "text-align": "center"
    }
)

# Animation buttons
fade_btn = Button("Fade In", on_click=fadeIn(id="anim-box", duration=600))
slide_btn = Button("Slide In", on_click=slideIn(id="anim-box", direction="left"))
shake_btn = Button("Shake", on_click=shake(id="anim-box", intensity=10))
pulse_btn = Button("Pulse", on_click=pulse(id="anim-box", scale=1.15, iterations=3))

# Sequence button
sequence_btn = Button("Combo", on_click=sequence(
    fadeIn(id="anim-box", duration=400),
    pulse(id="anim-box", scale=1.1, iterations=2),
    shake(id="anim-box", intensity=5, duration=400)
))

page = Page(Container(box, fade_btn, slide_btn, shake_btn, pulse_btn, sequence_btn))
app.add_page("index", page, index=True)

Animation Best Practices

  1. Use appropriate durations : Most animations work well between 300-600ms

    fadeIn(id="box", duration=400)  # Good - feels responsive
    fadeIn(id="box", duration=2000)  # Too slow - users will get impatient
    
  2. Match animation to context : Use subtle animations for frequent actions

    # Frequent action - subtle
    button.on_click = pulse(id="counter", scale=1.05, duration=200)
    
    # Important action - more dramatic
    success_btn.on_click = sequence(
        scaleIn(id="message", from_scale=0.5, duration=400),
        pulse(id="message", scale=1.1, iterations=2)
    )
    
  3. Don't overuse sequence : Long animation chains can frustrate users

    # Good - 2-3 animations
    sequence(fadeIn(id="a"), pulse(id="b"))
    
    # Avoid - too many steps
    sequence(fadeIn(id="a"), slideIn(id="b"), shake(id="c"), bounce(id="d"), fadeOut(id="e"))
    
  4. Provide visual feedback : Use animations to acknowledge user actions

    submit_btn.on_click = sequence(
        pulse(id="submit-btn", scale=0.95, duration=100),  # Button press feedback
        fadeOut(id="form", duration=300),
        fadeIn(id="success-message", duration=300)
    )
    

Available Animations Reference

Function Purpose Key Parameters
fadeIn Fade element in id , duration , easing
fadeOut Fade element out id , duration , hide_after
slideIn Slide element in id , direction , distance , duration
slideOut Slide element out id , direction , distance , duration
scaleIn Scale element in id , from_scale , duration
scaleOut Scale element out id , to_scale , duration
shake Shake effect id , intensity , duration
bounce Bounce effect id , distance , duration
pulse Pulse/heartbeat id , scale , duration , iterations
rotate Rotate element id , degrees , duration
flip Flip on axis id , axis , duration
colorChange Change color id , property , from_color , to_color
morphSize Change size id , to_width , to_height , duration
sequence Chain animations *animations

All animations return dScript objects and can be used with .then() for advanced chaining.