0
# Widgets and Utilities
1
2
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.
3
4
## Capabilities
5
6
### Image Widget
7
8
Enhanced image component with automatic format detection:
9
10
```python { .api }
11
def image(src: str, **attributes) -> VdomDict: ...
12
```
13
14
**Parameters:**
15
- `src`: Image source URL or path
16
- `**attributes`: Additional HTML attributes
17
18
**Returns:** VdomDict representing an img element with enhanced functionality
19
20
**Usage Examples:**
21
22
```python
23
from reactpy.widgets import image
24
25
# Basic image
26
photo = image("https://example.com/photo.jpg")
27
28
# Image with attributes
29
styled_image = image(
30
"logo.png",
31
alt="Company Logo",
32
width=200,
33
className="logo"
34
)
35
36
# Responsive image
37
responsive = image(
38
"hero.jpg",
39
style={"maxWidth": "100%", "height": "auto"}
40
)
41
```
42
43
### Linked Inputs
44
45
Create synchronized input components:
46
47
```python { .api }
48
def linked_inputs(*inputs) -> list[VdomDict]: ...
49
```
50
51
**Parameters:**
52
- `*inputs`: Input element configurations
53
54
**Returns:** List of synchronized input elements
55
56
**Usage Examples:**
57
58
```python
59
from reactpy import component, html, use_state
60
from reactpy.widgets import linked_inputs
61
62
@component
63
def SynchronizedInputs():
64
value, set_value = use_state("")
65
66
# Create linked inputs that share the same value
67
input1, input2, input3 = linked_inputs(
68
{"placeholder": "Input 1", "value": value, "onChange": set_value},
69
{"placeholder": "Input 2", "value": value, "onChange": set_value},
70
{"placeholder": "Input 3", "value": value, "onChange": set_value}
71
)
72
73
return html.div(
74
html.h3("Linked Inputs"),
75
html.p("Type in any input to see synchronization:"),
76
input1,
77
input2,
78
input3,
79
html.p(f"Current value: {value}")
80
)
81
```
82
83
### Ref Utility Class
84
85
Mutable reference container that persists across renders:
86
87
```python { .api }
88
class Ref[T]:
89
current: T
90
91
def __init__(self, initial_value: T = None): ...
92
```
93
94
**Attributes:**
95
- `current`: The mutable value stored in the reference
96
97
**Usage Examples:**
98
99
```python
100
from reactpy import component, html, use_ref, use_effect
101
from reactpy.utils import Ref
102
103
@component
104
def FocusableInput():
105
input_ref = use_ref(None)
106
click_count = use_ref(0)
107
108
def handle_focus():
109
if input_ref.current:
110
input_ref.current.focus()
111
112
def handle_click():
113
click_count.current += 1
114
print(f"Clicked {click_count.current} times")
115
116
return html.div(
117
html.input({"ref": input_ref, "placeholder": "I can be focused"}),
118
html.button({"onClick": handle_focus}, "Focus Input"),
119
html.button({"onClick": handle_click}, "Count Clicks")
120
)
121
122
# Manual Ref creation
123
manual_ref = Ref("initial value")
124
manual_ref.current = "updated value"
125
```
126
127
### Sample Application
128
129
Pre-built sample application demonstrating ReactPy features:
130
131
```python { .api }
132
def SampleApp() -> VdomDict: ...
133
```
134
135
**Returns:** Complete sample application component
136
137
**Usage Examples:**
138
139
```python
140
from reactpy import run
141
from reactpy.sample import SampleApp
142
143
# Run the sample application
144
run(SampleApp)
145
146
# Embed sample app in your own component
147
@component
148
def MyApp():
149
return html.div(
150
html.h1("My Application"),
151
html.div({"style": {"border": "1px solid #ccc", "padding": "20px"}},
152
SampleApp()
153
)
154
)
155
```
156
157
### HTML/VDOM Conversion Utilities
158
159
Convert between HTML strings and VDOM structures:
160
161
```python { .api }
162
def html_to_vdom(html_string: str) -> VdomDict: ...
163
def vdom_to_html(vdom_dict: VdomDict) -> str: ...
164
```
165
166
**Usage Examples:**
167
168
```python
169
from reactpy.utils import html_to_vdom, vdom_to_html
170
171
# Convert HTML string to VDOM
172
html_str = """
173
<div class="container">
174
<h1>Title</h1>
175
<p>Content here</p>
176
<button onclick="alert('clicked')">Click me</button>
177
</div>
178
"""
179
180
vdom_element = html_to_vdom(html_str)
181
182
# Convert VDOM back to HTML
183
html_output = vdom_to_html(vdom_element)
184
print(html_output)
185
186
# Use in components
187
@component
188
def HtmlContent():
189
dynamic_html = "<p>This is <strong>dynamic</strong> content</p>"
190
vdom_content = html_to_vdom(dynamic_html)
191
192
return html.div(
193
html.h2("Dynamic HTML Content"),
194
vdom_content
195
)
196
```
197
198
### Utility Patterns
199
200
Common patterns using widgets and utilities:
201
202
```python
203
@component
204
def FormWithValidation():
205
form_data, set_form_data = use_state({
206
"name": "",
207
"email": "",
208
"password": "",
209
"confirm_password": ""
210
})
211
212
# Create linked password inputs for confirmation
213
password_input, confirm_input = linked_inputs(
214
{
215
"type": "password",
216
"placeholder": "Password",
217
"value": form_data["password"],
218
"onChange": lambda e: update_field("password", e["target"]["value"])
219
},
220
{
221
"type": "password",
222
"placeholder": "Confirm Password",
223
"value": form_data["confirm_password"],
224
"onChange": lambda e: update_field("confirm_password", e["target"]["value"])
225
}
226
)
227
228
def update_field(field, value):
229
set_form_data({**form_data, field: value})
230
231
def validate_form():
232
return (
233
form_data["name"] and
234
form_data["email"] and
235
form_data["password"] and
236
form_data["password"] == form_data["confirm_password"]
237
)
238
239
return html.form(
240
html.div(
241
html.label("Name:"),
242
html.input({
243
"value": form_data["name"],
244
"onChange": lambda e: update_field("name", e["target"]["value"])
245
})
246
),
247
html.div(
248
html.label("Email:"),
249
html.input({
250
"type": "email",
251
"value": form_data["email"],
252
"onChange": lambda e: update_field("email", e["target"]["value"])
253
})
254
),
255
html.div(
256
html.label("Password:"),
257
password_input
258
),
259
html.div(
260
html.label("Confirm Password:"),
261
confirm_input
262
),
263
html.button(
264
{
265
"type": "submit",
266
"disabled": not validate_form()
267
},
268
"Submit"
269
)
270
)
271
272
@component
273
def ImageGallery(images):
274
return html.div(
275
{"className": "gallery"},
276
*[
277
image(
278
img_url,
279
key=img_url,
280
className="gallery-image",
281
alt=f"Gallery image {i+1}",
282
loading="lazy"
283
)
284
for i, img_url in enumerate(images)
285
]
286
)
287
288
@component
289
def DynamicContent():
290
html_content, set_html_content = use_state("")
291
292
def load_content():
293
# Simulate loading HTML from API
294
new_html = """
295
<div class="loaded-content">
296
<h3>Loaded Content</h3>
297
<p>This content was loaded dynamically!</p>
298
<ul>
299
<li>Item 1</li>
300
<li>Item 2</li>
301
<li>Item 3</li>
302
</ul>
303
</div>
304
"""
305
set_html_content(new_html)
306
307
return html.div(
308
html.button({"onClick": load_content}, "Load Content"),
309
html_to_vdom(html_content) if html_content else html.p("No content loaded")
310
)
311
```
312
313
### Custom Widget Creation
314
315
Create your own reusable widgets:
316
317
```python
318
def card(*children, title=None, className="card"):
319
"""Custom card widget"""
320
return html.div(
321
{"className": className},
322
html.h3(title) if title else None,
323
html.div({"className": "card-body"}, *children)
324
)
325
326
def button_group(*buttons, orientation="horizontal"):
327
"""Custom button group widget"""
328
class_name = f"btn-group btn-group-{orientation}"
329
return html.div(
330
{"className": class_name, "role": "group"},
331
*buttons
332
)
333
334
def data_table(data, columns):
335
"""Custom data table widget"""
336
return html.table(
337
{"className": "data-table"},
338
html.thead(
339
html.tr(
340
*[html.th(col["label"]) for col in columns]
341
)
342
),
343
html.tbody(
344
*[
345
html.tr(
346
*[html.td(str(row.get(col["key"], ""))) for col in columns],
347
key=f"row-{i}"
348
)
349
for i, row in enumerate(data)
350
]
351
)
352
)
353
354
# Usage of custom widgets
355
@component
356
def CustomWidgetExample():
357
users = [
358
{"id": 1, "name": "Alice", "email": "alice@example.com"},
359
{"id": 2, "name": "Bob", "email": "bob@example.com"}
360
]
361
362
columns = [
363
{"key": "id", "label": "ID"},
364
{"key": "name", "label": "Name"},
365
{"key": "email", "label": "Email"}
366
]
367
368
return html.div(
369
card(
370
data_table(users, columns),
371
title="User List"
372
),
373
button_group(
374
html.button("Add User"),
375
html.button("Edit User"),
376
html.button("Delete User")
377
)
378
)
379
```