or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

csrf.mdfields.mdforms.mdi18n.mdindex.mdvalidation.mdwidgets.md

widgets.mddocs/

0

# Widgets and Rendering

1

2

HTML rendering system with customizable widgets for different input types, from basic text inputs to complex multi-select components and custom layouts. Widgets handle the conversion of form fields into HTML markup with proper attributes and styling.

3

4

## Capabilities

5

6

### Widget Utilities

7

8

Core utilities for HTML generation and attribute handling.

9

10

```python { .api }

11

def html_params(**kwargs) -> str:

12

"""

13

Generate HTML attributes from keyword arguments.

14

15

Parameters:

16

- **kwargs: Attribute name-value pairs

17

18

Returns:

19

str: HTML attribute string

20

21

Example:

22

html_params(id="myfield", class_="form-control", required=True)

23

# Returns: 'id="myfield" class="form-control" required'

24

"""

25

```

26

27

### Base Widget Classes

28

29

Foundation widget classes that other widgets inherit from.

30

31

```python { .api }

32

class Widget:

33

"""

34

Base widget class for HTML rendering.

35

"""

36

def __call__(self, field, **kwargs) -> str:

37

"""

38

Render field as HTML.

39

40

Parameters:

41

- field: Field instance to render

42

- **kwargs: Additional HTML attributes

43

44

Returns:

45

str: HTML markup

46

"""

47

48

class Input(Widget):

49

"""

50

Base class for HTML input elements.

51

52

Parameters:

53

- input_type: HTML input type attribute

54

"""

55

def __init__(self, input_type=None): ...

56

def __call__(self, field, **kwargs) -> str: ...

57

```

58

59

### Text Input Widgets

60

61

Widgets for various text input types.

62

63

```python { .api }

64

class TextInput(Input):

65

"""

66

Standard text input widget.

67

Renders: <input type="text" />

68

"""

69

def __init__(self): ...

70

71

class PasswordInput(Input):

72

"""

73

Password input widget (hides input value).

74

Renders: <input type="password" />

75

76

Parameters:

77

- hide_value: Whether to hide value in HTML (default: True)

78

"""

79

def __init__(self, hide_value=True): ...

80

81

class HiddenInput(Input):

82

"""

83

Hidden input widget.

84

Renders: <input type="hidden" />

85

"""

86

def __init__(self): ...

87

88

class TextArea(Widget):

89

"""

90

Multi-line textarea widget.

91

Renders: <textarea></textarea>

92

"""

93

def __call__(self, field, **kwargs) -> str: ...

94

```

95

96

### HTML5 Input Widgets

97

98

Modern HTML5 input types with enhanced validation and user experience.

99

100

```python { .api }

101

class EmailInput(Input):

102

"""

103

HTML5 email input widget.

104

Renders: <input type="email" />

105

"""

106

def __init__(self): ...

107

108

class URLInput(Input):

109

"""

110

HTML5 URL input widget.

111

Renders: <input type="url" />

112

"""

113

def __init__(self): ...

114

115

class SearchInput(Input):

116

"""

117

HTML5 search input widget.

118

Renders: <input type="search" />

119

"""

120

def __init__(self): ...

121

122

class TelInput(Input):

123

"""

124

HTML5 telephone input widget.

125

Renders: <input type="tel" />

126

"""

127

def __init__(self): ...

128

129

class NumberInput(Input):

130

"""

131

HTML5 number input widget.

132

Renders: <input type="number" />

133

134

Parameters:

135

- step: Step value for numeric input

136

- min: Minimum value

137

- max: Maximum value

138

"""

139

def __init__(self, step=None, min=None, max=None): ...

140

141

class RangeInput(Input):

142

"""

143

HTML5 range input widget.

144

Renders: <input type="range" />

145

146

Parameters:

147

- step: Step value for range input

148

"""

149

def __init__(self, step=None): ...

150

151

class ColorInput(Input):

152

"""

153

HTML5 color input widget.

154

Renders: <input type="color" />

155

"""

156

def __init__(self): ...

157

```

158

159

### Date and Time Widgets

160

161

Widgets for date and time input with HTML5 support.

162

163

```python { .api }

164

class DateInput(Input):

165

"""

166

HTML5 date input widget.

167

Renders: <input type="date" />

168

"""

169

def __init__(self): ...

170

171

class TimeInput(Input):

172

"""

173

HTML5 time input widget.

174

Renders: <input type="time" />

175

"""

176

def __init__(self): ...

177

178

class DateTimeInput(Input):

179

"""

180

HTML5 datetime input widget.

181

Renders: <input type="datetime" />

182

"""

183

def __init__(self): ...

184

185

class DateTimeLocalInput(Input):

186

"""

187

HTML5 datetime-local input widget.

188

Renders: <input type="datetime-local" />

189

"""

190

def __init__(self): ...

191

192

class MonthInput(Input):

193

"""

194

HTML5 month input widget.

195

Renders: <input type="month" />

196

"""

197

def __init__(self): ...

198

199

class WeekInput(Input):

200

"""

201

HTML5 week input widget.

202

Renders: <input type="week" />

203

"""

204

def __init__(self): ...

205

```

206

207

### Choice and Selection Widgets

208

209

Widgets for selecting from multiple options.

210

211

```python { .api }

212

class CheckboxInput(Input):

213

"""

214

Checkbox input widget.

215

Renders: <input type="checkbox" />

216

"""

217

def __init__(self): ...

218

219

class RadioInput(Input):

220

"""

221

Radio input widget.

222

Renders: <input type="radio" />

223

"""

224

def __init__(self): ...

225

226

class Select(Widget):

227

"""

228

Select dropdown widget.

229

Renders: <select><option>...</option></select>

230

231

Parameters:

232

- multiple: Whether to allow multiple selections

233

"""

234

def __init__(self, multiple=False): ...

235

def __call__(self, field, **kwargs) -> str: ...

236

237

class Option(Widget):

238

"""

239

Option element for Select widget.

240

Renders: <option value="...">label</option>

241

"""

242

def __call__(self, field, **kwargs) -> str: ...

243

```

244

245

### File Input Widgets

246

247

Widgets for file uploads.

248

249

```python { .api }

250

class FileInput(Input):

251

"""

252

File input widget.

253

Renders: <input type="file" />

254

255

Parameters:

256

- multiple: Whether to allow multiple file selection

257

"""

258

def __init__(self, multiple=False): ...

259

```

260

261

### Button and Submit Widgets

262

263

Widgets for form submission and actions.

264

265

```python { .api }

266

class SubmitInput(Input):

267

"""

268

Submit button widget.

269

Renders: <input type="submit" />

270

"""

271

def __init__(self): ...

272

```

273

274

### Container Widgets

275

276

Widgets for organizing and grouping multiple fields.

277

278

```python { .api }

279

class ListWidget(Widget):

280

"""

281

Container widget that renders fields as list items.

282

Used for RadioField and CheckboxField groups.

283

284

Parameters:

285

- html_tag: HTML tag for container (default: "ul")

286

- prefix_label: Whether to put label before input (default: True)

287

"""

288

def __init__(self, html_tag="ul", prefix_label=True): ...

289

def __call__(self, field, **kwargs) -> str: ...

290

291

class TableWidget(Widget):

292

"""

293

Container widget that renders fields in table format.

294

Used for FormField to organize nested forms.

295

296

Parameters:

297

- with_table_tag: Whether to wrap in <table> tag (default: True)

298

"""

299

def __init__(self, with_table_tag=True): ...

300

def __call__(self, field, **kwargs) -> str: ...

301

```

302

303

## Widget Usage Examples

304

305

### Basic Widget Usage

306

307

```python

308

from wtforms import Form, StringField

309

from wtforms.widgets import TextInput, TextArea

310

311

class MyForm(Form):

312

# Default widget (TextInput)

313

name = StringField('Name')

314

315

# Custom widget

316

bio = StringField('Biography', widget=TextArea())

317

318

# Widget with custom attributes

319

email = StringField('Email',

320

widget=TextInput(),

321

render_kw={'placeholder': 'Enter your email', 'class': 'form-control'}

322

)

323

324

# Render fields

325

form = MyForm()

326

print(form.name()) # <input id="name" name="name" type="text" value="" />

327

print(form.bio()) # <textarea id="bio" name="bio"></textarea>

328

print(form.email()) # <input class="form-control" id="email" name="email" placeholder="Enter your email" type="text" value="" />

329

```

330

331

### HTML5 Input Widgets

332

333

```python

334

from wtforms import Form, StringField, IntegerField, DateField

335

from wtforms.widgets import EmailInput, NumberInput, DateInput, ColorInput

336

337

class ProfileForm(Form):

338

email = StringField('Email', widget=EmailInput())

339

website = StringField('Website', widget=URLInput())

340

age = IntegerField('Age', widget=NumberInput(min=13, max=120))

341

birth_date = DateField('Birth Date', widget=DateInput())

342

favorite_color = StringField('Favorite Color', widget=ColorInput())

343

344

# Range input with custom attributes

345

satisfaction = IntegerField('Satisfaction',

346

widget=RangeInput(step=1),

347

render_kw={'min': 1, 'max': 10}

348

)

349

350

# Generated HTML includes HTML5 validation attributes

351

form = ProfileForm()

352

print(form.age()) # <input id="age" max="120" min="13" name="age" type="number" value="" />

353

```

354

355

### Select and Choice Widgets

356

357

```python

358

from wtforms import Form, SelectField, SelectMultipleField

359

from wtforms.widgets import Select

360

361

class PreferencesForm(Form):

362

# Default Select widget

363

country = SelectField('Country', choices=[

364

('us', 'United States'),

365

('ca', 'Canada')

366

])

367

368

# Multiple selection

369

languages = SelectMultipleField('Languages',

370

choices=[

371

('en', 'English'),

372

('es', 'Spanish'),

373

('fr', 'French')

374

],

375

widget=Select(multiple=True)

376

)

377

378

form = PreferencesForm()

379

print(form.country())

380

# <select id="country" name="country">

381

# <option value="us">United States</option>

382

# <option value="ca">Canada</option>

383

# </select>

384

385

print(form.languages())

386

# <select id="languages" multiple name="languages">

387

# <option value="en">English</option>

388

# <option value="es">Spanish</option>

389

# <option value="fr">French</option>

390

# </select>

391

```

392

393

### Radio Button Lists

394

395

```python

396

from wtforms import Form, RadioField

397

from wtforms.widgets import ListWidget, RadioInput

398

399

class SurveyForm(Form):

400

rating = RadioField('Rating',

401

choices=[

402

('1', 'Poor'),

403

('2', 'Fair'),

404

('3', 'Good'),

405

('4', 'Very Good'),

406

('5', 'Excellent')

407

],

408

widget=ListWidget(prefix_label=False),

409

option_widget=RadioInput()

410

)

411

412

form = SurveyForm()

413

print(form.rating())

414

# <ul id="rating">

415

# <li><input id="rating-0" name="rating" type="radio" value="1"> <label for="rating-0">Poor</label></li>

416

# <li><input id="rating-1" name="rating" type="radio" value="2"> <label for="rating-1">Fair</label></li>

417

# ...

418

# </ul>

419

```

420

421

### File Upload Widgets

422

423

```python

424

from wtforms import Form, FileField, MultipleFileField

425

from wtforms.widgets import FileInput

426

427

class UploadForm(Form):

428

single_file = FileField('Upload File', widget=FileInput())

429

multiple_files = MultipleFileField('Upload Files',

430

widget=FileInput(multiple=True)

431

)

432

433

form = UploadForm()

434

print(form.single_file()) # <input id="single_file" name="single_file" type="file" />

435

print(form.multiple_files()) # <input id="multiple_files" multiple name="multiple_files" type="file" />

436

```

437

438

### Custom Widget Development

439

440

```python

441

from wtforms.widgets import Widget

442

from wtforms import Form, StringField

443

444

class StarRatingWidget(Widget):

445

"""Custom star rating widget."""

446

447

def __call__(self, field, **kwargs):

448

kwargs.setdefault('id', field.id)

449

html = []

450

451

# Add star rating HTML

452

html.append(f'<div class="star-rating" id="{field.id}">')

453

for i in range(1, 6):

454

checked = 'checked' if field.data == str(i) else ''

455

html.append(f'''

456

<input type="radio" name="{field.name}" value="{i}" {checked} />

457

<label for="{field.id}-{i}">★</label>

458

''')

459

html.append('</div>')

460

461

return ''.join(html)

462

463

class ReviewForm(Form):

464

rating = StringField('Rating', widget=StarRatingWidget())

465

comment = StringField('Comment')

466

467

form = ReviewForm()

468

print(form.rating()) # Custom star rating HTML

469

```

470

471

### Widget with Bootstrap Classes

472

473

```python

474

from wtforms import Form, StringField, SelectField, SubmitField

475

from wtforms.widgets import TextInput, Select, SubmitInput

476

477

class BootstrapForm(Form):

478

name = StringField('Name',

479

widget=TextInput(),

480

render_kw={'class': 'form-control', 'placeholder': 'Enter your name'}

481

)

482

483

category = SelectField('Category',

484

choices=[('tech', 'Technology'), ('science', 'Science')],

485

widget=Select(),

486

render_kw={'class': 'form-select'}

487

)

488

489

submit = SubmitField('Submit',

490

widget=SubmitInput(),

491

render_kw={'class': 'btn btn-primary'}

492

)

493

494

form = BootstrapForm()

495

print(form.name())

496

# <input class="form-control" id="name" name="name" placeholder="Enter your name" type="text" value="" />

497

```

498

499

### Conditional Widget Selection

500

501

```python

502

from wtforms import Form, StringField

503

from wtforms.widgets import TextInput, TextArea

504

505

class DynamicForm(Form):

506

content = StringField('Content')

507

508

def __init__(self, use_textarea=False, *args, **kwargs):

509

super().__init__(*args, **kwargs)

510

511

# Choose widget based on condition

512

if use_textarea:

513

self.content.widget = TextArea()

514

else:

515

self.content.widget = TextInput()

516

517

# Usage

518

short_form = DynamicForm(use_textarea=False)

519

long_form = DynamicForm(use_textarea=True)

520

521

print(short_form.content()) # <input type="text" ... />

522

print(long_form.content()) # <textarea>...</textarea>

523

```

524

525

### Widget Attribute Manipulation

526

527

```python

528

from wtforms import Form, StringField

529

from wtforms.widgets import html_params

530

531

class CustomForm(Form):

532

username = StringField('Username')

533

534

# Render with custom attributes

535

form = CustomForm()

536

field = form.username

537

538

# Method 1: Using render_kw

539

field.render_kw = {'class': 'form-control', 'data-validate': 'true'}

540

print(field())

541

542

# Method 2: Passing attributes directly

543

print(field(class_='form-control', placeholder='Enter username'))

544

545

# Method 3: Using html_params utility

546

attrs = html_params(id=field.id, name=field.name, class_='form-control', required=True)

547

print(f'<input {attrs} />')

548

```

549

550

### Nested Form Widgets

551

552

```python

553

from wtforms import Form, StringField, FormField

554

from wtforms.widgets import TableWidget

555

556

class AddressForm(Form):

557

street = StringField('Street')

558

city = StringField('City')

559

zipcode = StringField('Zip Code')

560

561

class UserForm(Form):

562

name = StringField('Name')

563

address = FormField(AddressForm, widget=TableWidget())

564

565

form = UserForm()

566

print(form.address())

567

# <table>

568

# <tr><th><label for="address-street">Street</label></th>

569

# <td><input id="address-street" name="address-street" type="text" value="" /></td></tr>

570

# <tr><th><label for="address-city">City</label></th>

571

# <td><input id="address-city" name="address-city" type="text" value="" /></td></tr>

572

# ...

573

# </table>

574

```

575

576

### Widget Theming and Styling

577

578

```python

579

from wtforms import Form, StringField, SelectField, BooleanField

580

from wtforms.widgets import CheckboxInput

581

582

class ThemedForm(Form):

583

# Material Design theme

584

name = StringField('Name', render_kw={

585

'class': 'mdc-text-field__input',

586

'placeholder': ' ' # Required for Material Design

587

})

588

589

# Tailwind CSS theme

590

category = SelectField('Category',

591

choices=[('a', 'Option A'), ('b', 'Option B')],

592

render_kw={

593

'class': 'block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500'

594

}

595

)

596

597

# Custom checkbox styling

598

agree = BooleanField('I agree',

599

widget=CheckboxInput(),

600

render_kw={

601

'class': 'h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded'

602

}

603

)

604

605

form = ThemedForm()

606

# Each field renders with appropriate CSS classes

607

```