Dars is a Full-Stack Python UI framework for building modern, interactive web applications entirely in Python. Seamlessly integrated with FastAPI, it lets you build complete applications with Server-Side Rendering (SSR), reactive SPA routing, static site generation, and a production-ready backend API — all from a single Python codebase.
Project description
Dars Framework
Dars is a Full-Stack Python UI framework for building modern, interactive web applications entirely in Python. Seamlessly integrated with FastAPI, it lets you build complete applications with Server-Side Rendering (SSR), reactive SPA routing, static site generation, and a production-ready backend API — all from a single Python codebase, with zero JavaScript required.
Official Website | Documentation Docs | Official Roadmap | Extension for VSCode here and OpenVSX version here
pip install dars-framework
Try Dars without installing anything — visit the Dars Playground
How It Works
- Build your UI using Python classes and components (
Text,Button,Container,Page,Each,Show,If, etc.). - Preview instantly with hot-reload using
app.rTimeCompile(). - Export your app to static/dynamic/SSR web files with a single CLI command.
- Use multipage layouts, scripts, hooks, and more — see docs for advanced features.
- One app, four deployment targets simultaneously: Dars supports Static Site Generation (SSG), Single-Page Application (SPA) routing, Server-Side Rendering (SSR) with FastAPI, and a full Backend API — all from the same Python codebase. Mix and match freely: export some pages as static HTML for SEO, serve others via SSR for dynamic content, and expose REST API endpoints alongside your UI.
- Production-grade Authentication: Build secure apps with built-in Multi-Auth, HttpOnly JWT cookies, CSRF protection, and role-based access control — all without writing Javascript.
- Full backend toolkit included:
useFetchfor declarative data fetching,FormValidatorfor client-side validation,Eachfor runtime list rendering from API responses,JsonStorefor file-backed persistence,UploadPipelinefor secure file uploads,SecurityHeadersMiddlewarefor HTTP security, andDarsEnvfor.envfile support. - Database & Models: Built-in SQLite ORM with
DarsModel, auto-generated CRUD API, and migration tracking. - Server Actions: Call Python backend functions directly from client-side events with automatic validation and auth.
- Middleware System: Production-grade middleware pipeline — Auth, CORS, Rate limiting, Logging, Compression, Security headers.
- For more information visit the Documentation
Quick Example: Your First App
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(
Text(
text=useValue("app.title_val"),
style="fs-[33px] text-black font-bold mb-[5px]",
),
Text(
text=useDynamic("app.count"),
style="fs-[48px] mt-5 mb-[12px]"
),
Button(
text="+1",
on_click=state.count.increment(1),
style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] cursor-pointer fs-[18px]",
),
Button(
text="-1",
on_click=state.count.decrement(1),
style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] cursor-pointer fs-[18px] mt-[5px]",
),
Button(
text="Reset",
on_click=state.reset(),
style="bg-[#3498db] text-white p-[15px] px-[30px] rounded-[8px] cursor-pointer fs-[18px] mt-[5px]",
),
style="flex flex-col items-center justify-center h-[100vh] ffam-[Arial] bg-[#f0f2f5]",
)
app.add_page("index", index(), title="index")
if __name__ == "__main__":
app.rTimeCompile()
State Management System
Dars features a powerful state management system designed for different use cases.
Hook-Based State Management
Modern, Pythonic state management for reactive updates. Best for counters, timers, and component interactions.
Hooks System:
useDynamic(): Reactive state binding for automatic UI updates.useValue(): Set initial values from state (non-reactive).useWatch(): Monitor state changes and trigger side effects.
from dars.all import *
app = App("State Demo")
state = State("counter", count=0)
@route("/")
def index():
return Page(
Text(text=useDynamic("counter.count"), style="fs-[24px]"),
Button("Increment", on_click=state.count.increment(1)),
Button("Decrement", on_click=state.count.decrement(1)),
Button("Reset", on_click=state.count.set(0)),
)
app.add_page("index", index())
if __name__ == "__main__":
app.rTimeCompile()
[!WARNING] Important: When using hooks, the State ID is used for binding. Do not use an ID that belongs to another unrelated component, as hooks use this ID as the State ID. Using a conflicting ID may cause unexpected behavior or state collisions.
For detailed documentation, visit the State Management Guide.
Animation System
Dars includes 15+ built-in animations that work seamlessly with state management:
from dars.all import fadeIn, fadeOut, pulse, shake, sequence
# Single animation
button.on_click = fadeIn(id="modal", duration=500)
# Chain multiple animations
button.on_click = sequence(
fadeIn(id="box"),
pulse(id="box", scale=1.2, iterations=2),
shake(id="box", intensity=5)
)
Available Animations: fadeIn, fadeOut, slideIn, slideOut, scaleIn, scaleOut, pulse, shake, bounce, rotate, flip, colorChange, morphSize, popIn, popOut
For complete animation documentation, visit the Animation Guide.
Routing System (SPA & SSR)
Dars offers a flexible routing system supporting Client-Side Routing (SPA), Server-Side Rendering (SSR), and Static Site Generation — all in one app.
Server-Side Rendering (SSR)
from dars.all import *
@route("/dashboard", route_type=RouteType.SSR)
def dashboard():
return Page(
Text("Server-Side Rendered Page"),
Button("Click Me", on_click=alert("Hello from Client")),
)
Client-Side Routing (SPA)
@route("/")
def home():
return Page(Text("Home Page"))
@route("/about")
def about():
return Page(Text("About Us"))
Nested Routes with Outlet
@route("/dashboard")
def dashboard():
return Page(
Text("Dashboard", style="fs-[24px]"),
Container(
Link("Settings", href="/dashboard/settings"),
Link("Profile", href="/dashboard/profile"),
),
Outlet(),
)
app.add_page("dashboard", dashboard(), index=True)
app.add_page("settings", settings_page, route="/dashboard/settings", parent="dashboard")
app.add_page("profile", profile_page, route="/dashboard/profile", parent="dashboard")
404 Error Handling
app.set_404_page(Page(
Text("Page not found", style="fs-[32px] text-red-500"),
Link("Go Home", href="/"),
))
Authentication & Security
Dars provides a production-grade, secure-by-default authentication system. Tokens never touch the browser's JavaScript context — they live exclusively in HttpOnly cookies managed by the browser itself.
- HttpOnly Cookie-Based Sessions with automatic CSRF protection
@requires_authDecorator — Protect any route, auto-injectsrequest.state.user@requires_role("admin")— Role-based access control- Multi-Auth Support — Multiple isolated auth schemes in the same app
- Pure Python JWT — Zero third-party dependencies
- Auto-generated Auth Endpoints —
/_dars/auth/{id}/login,/me,/logout,/refresh - Refresh Token Rotation with server-side revocation
- PBKDF2 Password Hashing — 100k iterations, timing-attack resistant
Quick Example
from dars.all import *
def verify_user(username, password):
if username == "admin" and password == "secret":
return {"id": "1", "username": "admin", "role": "admin"}
return None
@route("/dashboard", route_type=RouteType.SSR)
@requires_auth(verify_credentials_callback=verify_user, secret="super_secret")
async def dashboard(request: Request):
user = request.state.user # injected by @requires_auth
return Page(
Text(f"Welcome, {user['username']}!"),
)
For a complete working example, check out the /examples/FullStack-app in the repository.
For complete documentation, visit the Authentication Guide.
Server Actions
Call Python backend functions directly from client-side events with zero boilerplate:
from dars.backend.actions import server_action, call_server
@server_action(csrf_protected=False)
def greet(name: str, count: int = 1) -> list:
return [f"Hello {name}! x{i}" for i in range(count)]
# In your page:
Button("Greet", on_click=call_server("greet", name="World", count=3))
Features:
- Type validation via Pydantic models built from annotations
- Sync & async support
- Auth-protected actions with
@server_action(auth_required=True, roles=["admin"]) - CSRF protection for mutating actions
- Success/error callbacks —
call_server("action", on_success=..., on_error=...)
Database & Models (SQLite ORM)
Dars ships with a built-in declarative ORM for SQLite with auto-generated CRUD API:
from dars.backend.models import DarsModel, TextField, IntegerField
from dars.backend.database import Database
class Product(DarsModel):
__tablename__ = "products"
name = TextField(nullable=False)
price = IntegerField(default=0)
db = Database("app.db")
db.register(Product)
db.create_all()
# CRUD
product = Product(name="Widget", price=99)
product.save()
all_products = Product.objects.all()
Product.objects.filter(price=0)
# Auto-generate REST API
from dars.backend.models import register_model_api
register_model_api(app, db, prefix="/api/models")
Route Types & Guards
Dars supports four route types with client-side security guards:
from dars.core.route_types import RouteType
@route("/") # PUBLIC (default)
@route("/dashboard", route_type=RouteType.SSR) # Server-side rendered
@route("/account", route_type=RouteType.PRIVATE) # Auth required
@route("/admin", route_type=RouteType.PROTECTED, roles=["admin"]) # Auth + role
Middleware System
Production-grade middleware pipeline for FastAPI backends:
from dars.backend.middleware import (
AuthMiddleware, SecurityHeadersMiddleware,
CORSMiddleware, RateLimitMiddleware,
LoggingMiddleware, CompressionMiddleware,
)
app.add_middleware(AuthMiddleware, secret="your-secret", exclude_paths=["/_dars/auth"])
app.add_middleware(SecurityHeadersMiddleware, csp="default-src 'self'", hsts=True)
app.add_middleware(RateLimitMiddleware, calls_per_minute=60)
Custom Components
Function Components
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>
"""
card = UserCard("John Doe", "john@example.com", id="user-1", style="p-[20px]")
Control Flow Components
Show — Runtime Visibility Toggle
Always renders children into the DOM. A falsy VRef condition hides the wrapper with display:none. Reacts to VRef changes at runtime — perfect for loading spinners, error banners, and any UI that toggles based on live state.
from dars.all import *
is_loading = setVRef(True, ".loading")
has_error = setVRef(False, ".error")
Show(
is_loading,
Container(Spinner(), Text("Loading…"), style="flex items-center gap-2"),
)
Show(
has_error,
Container(
Text("Something went wrong.", style="text-red-600"),
style="bg-red-50 border rounded p-3",
),
)
Each — List Rendering from Python or API
Renders a list using a template function. Works with both compile-time Python lists and runtime VRef arrays (e.g. from useFetch).
Compile-time list:
users = [{"name": "Alice"}, {"name": "Bob"}]
Each(items=users, render=lambda u: Text(u["name"]))
Runtime VRef list (from API):
The render function is called at export time with a sentinel dict containing __item_<field>__ placeholders. At runtime, dom_each_render substitutes real values for each item. Use __item_done_class__ to get "line-through text-gray-400" for done items automatically.
tasks_vref = setVRef([], ".tasks-data") # filled by useFetch
def task_item(t):
title = t.get("title", "__item_title__") if isinstance(t, dict) else "__item_title__"
item_id = t.get("id", "__item_id__") if isinstance(t, dict) else "__item_id__"
return Container(
Text(title, style="flex: 1 1 0%", class_name="__item_done_class__"),
Text(f"#{item_id}", style="text-xs text-gray-400 ml-2"),
style="flex items-center gap-2 p-2 border rounded bg-white shadow-sm",
)
Each(items=tasks_vref, render=task_item, class_name="space-y-1 mb-6")
Supported API response shapes are automatically unwrapped: {"tasks":[...]}, {"items":[...]}, {"data":[...]}, {"results":[...]}, or a plain [...] array.
Backend & API Communication
Dars ships a complete production-grade backend toolkit. Everything is pure Python — no JavaScript required.
useFetch — Declarative Data Fetching
Fetch data from any API and bind the response to reactive VRefs automatically:
from dars.all import *
tasks_sel = ".tasks-data"
_tasks = setVRef([], tasks_sel)
trigger, loading, data, error = useFetch(
"/api/tasks",
on_success=runSequence(
updateVRef(".loading", False),
updateVRefFromResponse(tasks_sel), # stores API response → VRef
),
on_error=runSequence(
updateVRef(".loading", False),
updateVRef(".error", True),
),
)
page = Page(
Show(loading, Spinner()),
Show(error, Text("Could not load tasks.", style="text-red-500")),
Each(items=_tasks, render=lambda t: Container(
Text(t.get("title", "__item_title__"), style="flex-1"),
Text(f"#{t.get('id', '__item_id__')}", style="text-xs text-gray-400"),
style="flex gap-2 p-2 border rounded bg-white",
)),
Button("↻ Refresh", on_click=trigger),
)
page.add_script(trigger) # auto-run on page load
FormValidator — Client-Side Validation
Validate forms before submitting — rules declared once in Python, enforced both client-side and server-side:
validator = FormValidator({
"email": [required(), email()],
"password": [required(), min_length(8)],
})
submit = validator.validated_submit(
url="/api/login",
form_data=collect_form(email=V("#email"), password=V("#password")),
on_success=goTo("/dashboard"),
on_error=setText("error-msg", "Login failed."),
)
page = Page(
Input(id="email", placeholder="Email"),
Text("", id="email-error", style="text-red-500 text-sm"),
Input(id="password", placeholder="Password"),
Text("", id="password-error", style="text-red-500 text-sm"),
Text("", id="error-msg", style="text-red-500 text-sm"),
Button("Login", on_click=submit),
)
JsonStore — File-Backed Persistence
Thread-safe, atomic-write JSON storage for rapid prototyping and small backends:
from dars.backend.store import JsonStore
store = JsonStore("tasks.json", default={"tasks": []})
tasks = store.get("tasks", [])
tasks.append({"id": 1, "title": "New task", "done": False})
store.set("tasks", tasks)
UploadPipeline — Secure File Uploads
from dars.backend.upload import UploadPipeline
pipeline = UploadPipeline(
upload_dir="uploads",
allowed_types=["image/png", "image/jpeg", "application/pdf"],
max_size_bytes=10 * 1024 * 1024,
)
pipeline.create_endpoint(app, path="/api/upload")
Frontend component:
FileUpload(
upload_url="/api/upload",
accepted_types=["image/png", "image/jpeg"],
max_size_bytes=5 * 1024 * 1024,
on_upload_complete=setText("status", "Uploaded!"),
on_upload_error=setText("status", "Upload failed."),
)
SecurityHeadersMiddleware — HTTP Security
from dars.backend.ssr import SSRApp
ssr = SSRApp(dars_app, prefix="/api/ssr")
ssr.use_cors(origins=["http://localhost:4000"], credentials=True)
ssr.use_security_headers() # X-Frame-Options, CSP, HSTS, etc.
ssr.use_upload(upload_dir="uploads", allowed_types=["image/*"])
app = ssr.fastapi_app
DarsEnv — Environment Variables
from dars.env import DarsEnv
DarsEnv.load() # loads .env silently if present
api_key = DarsEnv.get("API_KEY") # None if missing
secret = DarsEnv.require("SECRET") # raises KeyError if missing
Full Backend API Example
from dars.backend.ssr import SSRApp
from dars.backend.store import JsonStore
from main import app as dars_app
from fastapi import Request
from fastapi.responses import JSONResponse
import os
ssr = SSRApp(dars_app, prefix="/api/ssr")
ssr.use_cors(origins=["http://localhost:4000"], credentials=True)
ssr.use_security_headers()
app = ssr.fastapi_app
# ── Production: serve dist/ as static files with SPA fallback ───────────────
from backend.apiConfig import DarsEnv
if not DarsEnv.is_dev():
ssr.use_spa_fallback()
# ────────────────────────────────────────────────────────────────────────────
_store = JsonStore("tasks_db.json", default={"tasks": []})
@app.get("/api/tasks")
async def get_tasks():
return JSONResponse({"tasks": _store.get("tasks", [])})
@app.post("/api/tasks")
async def create_task(request: Request):
body = await request.json()
tasks = _store.get("tasks", [])
task = {"id": len(tasks) + 1, "title": body.get("title", ""), "done": False}
tasks.append(task)
_store.set("tasks", tasks)
return JSONResponse(task, status_code=201)
@app.put("/api/tasks/{task_id}")
async def update_task(task_id: int, request: Request):
body = await request.json()
tasks = _store.get("tasks", [])
for t in tasks:
if t["id"] == task_id:
t.update({k: v for k, v in body.items() if k != "id"})
_store.set("tasks", tasks)
return JSONResponse(t)
return JSONResponse({"error": "Not found"}, status_code=404)
@app.delete("/api/tasks/{task_id}")
async def delete_task(task_id: int):
tasks = [t for t in _store.get("tasks", []) if t["id"] != task_id]
_store.set("tasks", tasks)
return JSONResponse({"deleted": task_id})
if __name__ == "__main__":
import uvicorn
print("\n" + "=" * 60)
print("Dars Fullstack Backend")
print("=" * 60)
if DarsEnv.is_dev():
port, host = 3000, "127.0.0.1"
else:
port, host = 8000, "0.0.0.0"
print("=" * 60 + "\n")
uvicorn.run(app, host=host, port=port)
For complete backend documentation, see the Backend & API Guide.
CLI Usage
| Command | What it does |
|---|---|
dars export my_app.py --format html |
Export app to HTML/CSS/JS in ./my_app_web |
dars init --type fullstack |
Scaffold full-stack project (SPA + SSR + API) |
dars preview |
Preview exported app (auto-detects output) |
dars preview --port 9000 |
Preview on a custom port |
dars init my_project |
Create a new Dars project |
dars init --update |
Create/Update dars.config.json in current dir |
dars build |
Build using dars.config.json |
dars config validate |
Validate dars.config.json and print report |
dars info my_app.py |
Show info about your app |
dars formats |
List supported export formats |
dars dev |
Run the configured entry file with hot preview |
dars dev --port 9000 |
Run dev server on a custom port |
dars dev --backend |
Deprecated: use dars dev with backendEntry configured. |
dars generate component <name> |
Scaffold a new FunctionComponent |
dars generate page <name> |
Scaffold a new page (static, SPA, or SSR) |
dars --help |
Show help and all CLI options |
Tip: use dars doctor to review optional tooling that can enhance bundling/minification.
Running with Backend
# Run both frontend and backend together in development
# (backendEntry must be configured in dars.config.json)
dars dev
Note:
dars dev --backendis deprecated in v1.9.14.dars devnow starts the fullstack development server automatically whenbackendEntryis configured.
Local Execution and Live Preview
if __name__ == "__main__":
app.rTimeCompile()
python my_app.py
# → http://localhost:8000
python my_app.py --port 8088
# Auto-detects output directory and port from config
dars preview
# Or specify a custom path and port
dars preview ./my_exported_app -p 8080
Project Configuration (dars.config.json)
{
"entry": "main.py",
"format": "html",
"outdir": "dist",
"publicDir": null,
"include": [],
"exclude": ["**/__pycache__", ".git", ".venv", "node_modules"],
"bundle": true,
"minify": true,
"utility_styles": {},
"markdownHighlight": true,
"markdownHighlightTheme": "auto",
"port": 8000,
"backendEntry": "backend.api:app"
}
| Key | Description |
|---|---|
entry |
Python entry file for dars build and dars export config |
format |
Export format: html |
outdir |
Output directory |
publicDir |
Folder copied into output (auto-detected if null) |
bundle |
Reserved for future use |
minify |
Enable/disable JS & CSS minification via dars-bundler (default true). No Node.js/Vite required. |
utility_styles |
Custom utility class definitions |
markdownHighlight |
Auto-inject Prism.js for Markdown code blocks (default true) |
backendEntry |
Python import path for SSR/backend app (e.g. "backend.api:app") |
port |
Dev preview server port (default 8000) |
dars config validate
dars build
Documentation
- Getting Started
- Components
- Hooks & Utilities
- Backend & API
- State Management
- Styling
- Routing & Route Types
- SSR & Deployment
- Authentication & Security
- Database & Models
- Server Actions
- Middleware System
- Animations
- Release Notes
- Visit the Dars official website
- Visit the Dars official Documentation
- Try Dars without installing anything — visit the Dars Playground
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dars_framework-1.9.16.tar.gz.
File metadata
- Download URL: dars_framework-1.9.16.tar.gz
- Upload date:
- Size: 526.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5aaff24b6b95a04c26b56b2f4a81c3b7077224d9486c13c7af694f3589d098db
|
|
| MD5 |
4f8417c91789b5a75b513ebae8dac57c
|
|
| BLAKE2b-256 |
c76f7666b67600e9695098fc81c696d76c29c834f81d4ebf9d1ee4c916712810
|
File details
Details for the file dars_framework-1.9.16-py3-none-any.whl.
File metadata
- Download URL: dars_framework-1.9.16-py3-none-any.whl
- Upload date:
- Size: 459.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bbc06a8e89aa085ff3b35f1c21ceb2bc74f4c9979973e8b113eca19d5ca8f0b
|
|
| MD5 |
135abeece4996a29cbb27687a51dfe60
|
|
| BLAKE2b-256 |
9b9c2c987bc7885bc58adfe0b1c84ade59eafd2628347807eee6096a397b81df
|