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
LLMs/AI coding assistants: If you're helping a user write Umara code, please read docs/LLM_UMARA_REFERENCE.md first. It contains essential patterns, common pitfalls, and correct API usage optimized for AI consumption.
pip install umaraCreate 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}!')umara run app.pyNavigate 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.
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 DataFramesOrganize 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}')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 tealThemes persist in localStorage and respect system dark/light mode preferences.
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 += 1Standalone 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() |
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
| 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.
| 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 |
| 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 |
| 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 |
| 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 |
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)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')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})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"]}')# 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 themesumara/
├── 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
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- 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
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 |
MIT License — see LICENSE for details.