Reactive user interfaces with pure Python
—
Pre-built components and utility functions for common use cases and data manipulation. ReactPy provides ready-to-use widgets and helpful utilities to accelerate development.
Enhanced image component with automatic format detection:
def image(src: str, **attributes) -> VdomDict: ...Parameters:
src: Image source URL or path**attributes: Additional HTML attributesReturns: VdomDict representing an img element with enhanced functionality
Usage Examples:
from reactpy.widgets import image
# Basic image
photo = image("https://example.com/photo.jpg")
# Image with attributes
styled_image = image(
"logo.png",
alt="Company Logo",
width=200,
className="logo"
)
# Responsive image
responsive = image(
"hero.jpg",
style={"maxWidth": "100%", "height": "auto"}
)Create synchronized input components:
def linked_inputs(*inputs) -> list[VdomDict]: ...Parameters:
*inputs: Input element configurationsReturns: List of synchronized input elements
Usage Examples:
from reactpy import component, html, use_state
from reactpy.widgets import linked_inputs
@component
def SynchronizedInputs():
value, set_value = use_state("")
# Create linked inputs that share the same value
input1, input2, input3 = linked_inputs(
{"placeholder": "Input 1", "value": value, "onChange": set_value},
{"placeholder": "Input 2", "value": value, "onChange": set_value},
{"placeholder": "Input 3", "value": value, "onChange": set_value}
)
return html.div(
html.h3("Linked Inputs"),
html.p("Type in any input to see synchronization:"),
input1,
input2,
input3,
html.p(f"Current value: {value}")
)Mutable reference container that persists across renders:
class Ref[T]:
current: T
def __init__(self, initial_value: T = None): ...Attributes:
current: The mutable value stored in the referenceUsage Examples:
from reactpy import component, html, use_ref, use_effect
from reactpy.utils import Ref
@component
def FocusableInput():
input_ref = use_ref(None)
click_count = use_ref(0)
def handle_focus():
if input_ref.current:
input_ref.current.focus()
def handle_click():
click_count.current += 1
print(f"Clicked {click_count.current} times")
return html.div(
html.input({"ref": input_ref, "placeholder": "I can be focused"}),
html.button({"onClick": handle_focus}, "Focus Input"),
html.button({"onClick": handle_click}, "Count Clicks")
)
# Manual Ref creation
manual_ref = Ref("initial value")
manual_ref.current = "updated value"Pre-built sample application demonstrating ReactPy features:
def SampleApp() -> VdomDict: ...Returns: Complete sample application component
Usage Examples:
from reactpy import run
from reactpy.sample import SampleApp
# Run the sample application
run(SampleApp)
# Embed sample app in your own component
@component
def MyApp():
return html.div(
html.h1("My Application"),
html.div({"style": {"border": "1px solid #ccc", "padding": "20px"}},
SampleApp()
)
)Convert between HTML strings and VDOM structures:
def html_to_vdom(html_string: str) -> VdomDict: ...
def vdom_to_html(vdom_dict: VdomDict) -> str: ...Usage Examples:
from reactpy.utils import html_to_vdom, vdom_to_html
# Convert HTML string to VDOM
html_str = """
<div class="container">
<h1>Title</h1>
<p>Content here</p>
<button onclick="alert('clicked')">Click me</button>
</div>
"""
vdom_element = html_to_vdom(html_str)
# Convert VDOM back to HTML
html_output = vdom_to_html(vdom_element)
print(html_output)
# Use in components
@component
def HtmlContent():
dynamic_html = "<p>This is <strong>dynamic</strong> content</p>"
vdom_content = html_to_vdom(dynamic_html)
return html.div(
html.h2("Dynamic HTML Content"),
vdom_content
)Common patterns using widgets and utilities:
@component
def FormWithValidation():
form_data, set_form_data = use_state({
"name": "",
"email": "",
"password": "",
"confirm_password": ""
})
# Create linked password inputs for confirmation
password_input, confirm_input = linked_inputs(
{
"type": "password",
"placeholder": "Password",
"value": form_data["password"],
"onChange": lambda e: update_field("password", e["target"]["value"])
},
{
"type": "password",
"placeholder": "Confirm Password",
"value": form_data["confirm_password"],
"onChange": lambda e: update_field("confirm_password", e["target"]["value"])
}
)
def update_field(field, value):
set_form_data({**form_data, field: value})
def validate_form():
return (
form_data["name"] and
form_data["email"] and
form_data["password"] and
form_data["password"] == form_data["confirm_password"]
)
return html.form(
html.div(
html.label("Name:"),
html.input({
"value": form_data["name"],
"onChange": lambda e: update_field("name", e["target"]["value"])
})
),
html.div(
html.label("Email:"),
html.input({
"type": "email",
"value": form_data["email"],
"onChange": lambda e: update_field("email", e["target"]["value"])
})
),
html.div(
html.label("Password:"),
password_input
),
html.div(
html.label("Confirm Password:"),
confirm_input
),
html.button(
{
"type": "submit",
"disabled": not validate_form()
},
"Submit"
)
)
@component
def ImageGallery(images):
return html.div(
{"className": "gallery"},
*[
image(
img_url,
key=img_url,
className="gallery-image",
alt=f"Gallery image {i+1}",
loading="lazy"
)
for i, img_url in enumerate(images)
]
)
@component
def DynamicContent():
html_content, set_html_content = use_state("")
def load_content():
# Simulate loading HTML from API
new_html = """
<div class="loaded-content">
<h3>Loaded Content</h3>
<p>This content was loaded dynamically!</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
"""
set_html_content(new_html)
return html.div(
html.button({"onClick": load_content}, "Load Content"),
html_to_vdom(html_content) if html_content else html.p("No content loaded")
)Create your own reusable widgets:
def card(*children, title=None, className="card"):
"""Custom card widget"""
return html.div(
{"className": className},
html.h3(title) if title else None,
html.div({"className": "card-body"}, *children)
)
def button_group(*buttons, orientation="horizontal"):
"""Custom button group widget"""
class_name = f"btn-group btn-group-{orientation}"
return html.div(
{"className": class_name, "role": "group"},
*buttons
)
def data_table(data, columns):
"""Custom data table widget"""
return html.table(
{"className": "data-table"},
html.thead(
html.tr(
*[html.th(col["label"]) for col in columns]
)
),
html.tbody(
*[
html.tr(
*[html.td(str(row.get(col["key"], ""))) for col in columns],
key=f"row-{i}"
)
for i, row in enumerate(data)
]
)
)
# Usage of custom widgets
@component
def CustomWidgetExample():
users = [
{"id": 1, "name": "Alice", "email": "alice@example.com"},
{"id": 2, "name": "Bob", "email": "bob@example.com"}
]
columns = [
{"key": "id", "label": "ID"},
{"key": "name", "label": "Name"},
{"key": "email", "label": "Email"}
]
return html.div(
card(
data_table(users, columns),
title="User List"
),
button_group(
html.button("Add User"),
html.button("Edit User"),
html.button("Delete User")
)
)Install with Tessl CLI
npx tessl i tessl/pypi-reactpy