A beautiful, modern Python framework for creating web UIs
Project description
Umara
Beautiful Python UIs — Without the Complexity
What is Umara?
Umara is a modern Python framework for building beautiful web applications with pure Python. No HTML, CSS, or JavaScript required.
import umara as um
um.header('Hello, Umara!')
with um.form('greeting'):
name = um.input('Your name')
if um.form_submit_button('Greet'):
um.success(f'Welcome, {name}!')
Why Umara?
- Beautiful by Default — Components look polished out of the box with modern design
- Fast & Reactive — WebSocket-based architecture for instant UI updates
- 12 Built-in Themes — Professional themes including dark mode, ocean, forest, and more
- Flexible Layouts — Columns, grids, cards, tabs, sidebars with precise control
- Hot Reload — See changes instantly during development
- Smart State — Efficient updates without full page re-runs
Getting Started
1. Install Umara
pip install umara
2. Create Your App
Create a file called app.py:
import umara as um
um.set_theme('ocean')
um.header('My First App')
um.text('Building beautiful UIs is easy!')
with um.card():
with um.form('hello_form'):
name = um.input('Enter your name', placeholder='John Doe')
if um.form_submit_button('Say Hello'):
um.success(f'Hello, {name}!')
3. Run Your App
umara run app.py
4. Open in Browser
Navigate to http://localhost:8501 in your browser.
That's it! Your app is running with hot reload enabled — any changes to app.py will automatically refresh in the browser.
Core Concepts
Components
Umara provides 120+ components for building UIs:
# Typography
um.title('Page Title')
um.header('Section Header')
um.text('Regular text')
# Inputs
name = um.input('Name', placeholder='Enter name...')
age = um.slider('Age', 0, 100, 25)
color = um.select('Color', ['Red', 'Green', 'Blue'])
agreed = um.checkbox('I agree')
# Feedback
um.success('Operation completed!')
um.error('Something went wrong')
um.warning('Please check your input')
um.info('Helpful tip here')
# Data Display
um.metric('Users', '12,543', delta=12.5)
um.progress(75, label='Completion')
um.dataframe(data) # Works with pandas DataFrames
Layouts
Organize content with flexible layout components:
# Columns
with um.columns(3):
with um.column():
um.metric('Users', '1,234')
with um.column():
um.metric('Revenue', '$5,678')
with um.column():
um.metric('Growth', '12.5%')
# Cards
with um.card(title='Dashboard'):
um.text('Card content here')
# Tabs
with um.tabs(['Overview', 'Data', 'Settings']):
with um.tab('Overview'):
um.text('Overview content')
with um.tab('Data'):
um.dataframe(data)
with um.tab('Settings'):
um.toggle('Enable feature', key='feature_toggle')
# Grid
with um.grid(columns=4, gap='16px'):
for i in range(8):
with um.card():
um.text(f'Item {i + 1}')
Themes
Switch between 12 professional themes:
um.set_theme('light') # Clean, minimal
um.set_theme('dark') # Modern dark mode
um.set_theme('ocean') # Calming blues
um.set_theme('forest') # Earthy greens
um.set_theme('slate') # Corporate gray
um.set_theme('nord') # Arctic, Scandinavian
um.set_theme('midnight') # Deep purple dark
um.set_theme('rose') # Warm pink
um.set_theme('copper') # Premium bronze
um.set_theme('lavender') # Soft purple
um.set_theme('sunset') # Warm orange
um.set_theme('mint') # Fresh teal
Themes persist in localStorage and respect system dark/light mode preferences.
State Management
Use session_state to persist data across interactions:
# Initialize state
um.session_state.setdefault('counter', 0)
# Display current value
um.text(f'Count: {um.session_state.counter}')
# Update state
if um.button('Increment'):
um.session_state.counter += 1
Keys and Forms
Standalone inputs require a key parameter to persist values across reruns:
# Without key - value resets on every rerun (not recommended)
name = um.input('Name')
# With key - value persists across reruns (recommended)
name = um.input('Name', key='user_name')
Use forms for input + button patterns to ensure values are captured reliably:
# RECOMMENDED: Form batches inputs and submits together
with um.form('contact'):
name = um.input('Name', key='name')
email = um.input('Email', key='email')
if um.form_submit_button('Submit'):
# All values guaranteed to be current
um.success(f'Submitted: {name}, {email}')
Why forms for input + button? Standalone inputs have a 50ms debounce. If users click a button immediately after typing, the input value may not be synced yet. Forms batch all values and submit them together, avoiding this race condition.
When to use which:
| Scenario | Use |
|---|---|
| Input + button action | um.form() (recommended) |
| Real-time filtering (on every keystroke) | Standalone with key |
| Toggle/checkbox immediate effect | Standalone with key |
| Multi-field data entry | um.form() |
Documentation
Full API Reference
See docs/UMARA_COMPLETE_REFERENCE.md for complete documentation including:
- All 120+ components with parameters and examples
- State management and caching
- Theming and custom styles
- Database and API connections
- Fragments for partial reruns
- Best practices
Quick Reference
Input Components
| Component | Description | Returns |
|---|---|---|
um.input(label, key=...) |
Text input field | str |
um.text_area(label, key=...) |
Multi-line text | str |
um.number_input(label, key=...) |
Numeric input | float |
um.slider(label, min, max, value, key=...) |
Range slider | float |
um.select(label, options, key=...) |
Dropdown select | str |
um.multiselect(label, options, key=...) |
Multi-select | list[str] |
um.checkbox(label, key=...) |
Checkbox | bool |
um.toggle(label, key=...) |
Toggle switch | bool |
um.radio(label, options, key=...) |
Radio buttons | str |
um.date_input(label, key=...) |
Date picker | str |
um.time_input(label, key=...) |
Time picker | str |
um.color_picker(label, key=...) |
Color picker | str |
um.file_uploader(label, key=...) |
File upload | file | None |
um.button(label, key=...) |
Click button | bool |
Important: Use key parameter for inputs outside forms to persist values across reruns.
Display Components
| Component | Description |
|---|---|
um.title(text) |
Large page title |
um.header(text) |
Section header |
um.subheader(text) |
Subsection header |
um.text(text) |
Regular text |
um.markdown(text) |
Markdown content |
um.code(code, language) |
Syntax-highlighted code |
um.metric(label, value, delta) |
Metric with trend |
um.progress(value, label) |
Progress bar |
um.dataframe(data) |
Data table (sortable) |
um.json_viewer(data) |
JSON tree view |
Feedback Components
| Component | Description |
|---|---|
um.success(message) |
Green success alert |
um.error(message) |
Red error alert |
um.warning(message) |
Yellow warning alert |
um.info(message) |
Blue info alert |
um.toast(message) |
Temporary notification |
um.spinner(text) |
Loading spinner |
Layout Components
| Component | Description |
|---|---|
um.columns(count) |
Multi-column layout |
um.grid(columns) |
CSS grid layout |
um.card(title) |
Card container |
um.tabs(names) |
Tabbed interface |
um.expander(title) |
Collapsible section |
um.sidebar() |
Side navigation |
um.modal(title, key) |
Modal dialog |
um.form(key) |
Form container |
Charts
| Component | Description |
|---|---|
um.line_chart(data, x, y) |
Line chart |
um.bar_chart(data, x, y) |
Bar chart |
um.area_chart(data, x, y) |
Area chart |
um.pie_chart(data, label, value) |
Pie chart |
um.scatter_chart(data, x, y) |
Scatter plot |
um.plotly_chart(figure) |
Plotly figure |
Examples
Dashboard
import umara as um
um.set_theme('dark')
um.header('Analytics Dashboard')
# Metrics row
with um.columns(4):
for label, value, delta in [
('Users', '12,543', 12.5),
('Revenue', '$48.2K', 8.2),
('Sessions', '1,892', -2.4),
('Conversion', '3.24%', 0.5),
]:
with um.column():
with um.card():
um.metric(label, value, delta=delta)
# Chart
um.subheader('Revenue Trend')
um.line_chart(data, x='month', y='revenue')
# Data table
um.subheader('Recent Orders')
um.dataframe(orders, sortable=True)
Form
import umara as um
um.header('Contact Form')
with um.card():
with um.form('contact'):
name = um.input('Name', key='name')
email = um.input('Email', type='email', key='email')
message = um.text_area('Message', key='message')
if um.form_submit_button('Send'):
if name and email and message:
um.success('Message sent!')
else:
um.error('Please fill all fields')
Chat Interface
import umara as um
um.set_theme('dark')
um.header('AI Chat')
# Initialize messages
um.session_state.setdefault('messages', [])
# Display chat
with um.chat_container(height='400px'):
for msg in um.session_state.messages:
um.chat_message(msg['content'], role=msg['role'])
# Input
user_input = um.chat_input('Type a message...')
if user_input:
um.session_state.messages.append({'role': 'user', 'content': user_input})
# Add your AI response logic here
response = "This is a response"
um.session_state.messages.append({'role': 'assistant', 'content': response})
File Upload with Size Limit
import umara as um
# Max 5MB file size
uploaded = um.file_uploader(
'Upload Document',
accept=['.pdf', '.docx'],
max_file_size=5 * 1024 * 1024 # 5MB in bytes
)
if uploaded:
um.success(f'Uploaded: {uploaded["name"]}')
CLI Commands
# Run an app
umara run app.py
# Run with custom host/port
umara run app.py --host 0.0.0.0 --port 8080
# Create new project
umara init my_project
# List available themes
umara themes
Project Structure
umara/
├── umara/ # Python package
│ ├── core.py # App lifecycle & component tree
│ ├── components.py # 100+ UI components
│ ├── server.py # WebSocket server
│ ├── frontend.py # Frontend HTML/CSS/JS
│ ├── state.py # State management
│ ├── themes.py # 12 built-in themes
│ └── cli.py # CLI commands
├── examples/ # Example applications
└── docs/ # Documentation
Contributing
Contributions are welcome! Here's how to set up for development:
# Clone the repository
git clone https://github.com/lhassa8/umara.git
cd umara
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install in development mode
pip install -e ".[dev]"
# Run tests
pytest
# Run the demo app
umara run examples/demo_app.py
# Open http://localhost:8501 in your browser
Roadmap
- 100+ UI components
- 12 built-in themes
- Charts & data visualization
- Chat/conversation components
- Forms with batched submission
- File uploads with size limits
- Sortable data tables
- ARIA accessibility labels
- System theme detection
- Theme persistence (localStorage)
- Authentication helpers
- Multi-page app support
- Component marketplace
- VS Code extension
Known Issues
The following issues are currently being tracked:
| Issue | Status | Workaround |
|---|---|---|
file_uploader() UI not visible |
Open | File upload functionality is limited; upload UI may not render |
| Modal/dialog in complex layouts | Open | Avoid nesting modals inside deeply nested column layouts |
License
MIT License — see LICENSE for details.
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 umara-0.5.0.tar.gz.
File metadata
- Download URL: umara-0.5.0.tar.gz
- Upload date:
- Size: 88.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c79e68c276dc8df1a98a18ff5dee8e327d7440e77ee3f33d32a36f01ca103920
|
|
| MD5 |
543b4c1f7704c5e11820b82a5230e491
|
|
| BLAKE2b-256 |
08bfe868710626e6c88c45e0b1459e3ac6eab5012a115b0959879abebe2691d8
|
File details
Details for the file umara-0.5.0-py3-none-any.whl.
File metadata
- Download URL: umara-0.5.0-py3-none-any.whl
- Upload date:
- Size: 92.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2bb8a25b3a26d10ca36122794089ab75a02fe8ac854101d395234709ebb422e4
|
|
| MD5 |
f1c2dcfcc224efb5c499cbbabbef0284
|
|
| BLAKE2b-256 |
6cd82b6334b30b78034851fd9ea524ea6c2aca54119ac977dda096fd0e5ada06
|