or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backend.mdcomponents.mdconfiguration.mdevents.mdhooks.mdhtml-elements.mdindex.mdsvg-elements.mdtesting.mdvdom.mdweb-modules.mdwidgets.md

widgets.mddocs/

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

```