or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compilation.mdcomponent-factory.mdcomponent-registration.mdindex.mdmounting-lifecycle.mdplugin-system.mdpure-components.mdutilities.md

pure-components.mddocs/

0

# Pure Components

1

2

Lightweight component pattern for simple, stateless components without full lifecycle management. Pure components are optimized for performance and simplicity, ideal for presentational components or when you need fine-grained control over rendering.

3

4

## Capabilities

5

6

### Pure Component Factory

7

8

Creates a pure component factory function that produces lightweight component instances.

9

10

```typescript { .api }

11

/**

12

* Lift a riot component interface into a pure riot object

13

* @param func - RiotPureComponent factory function

14

* @returns The original function marked as pure for internal handling

15

*/

16

function pure<

17

InitialProps extends DefaultProps = DefaultProps,

18

Context = any,

19

FactoryFunction = PureComponentFactoryFunction<InitialProps, Context>

20

>(func: FactoryFunction): FactoryFunction;

21

```

22

23

**Usage Example:**

24

25

```javascript

26

import { pure } from "riot";

27

28

// Create a pure component factory

29

const createSimpleCard = pure(({ slots, attributes, props }) => {

30

return {

31

mount(element, context) {

32

element.innerHTML = `

33

<div class="card">

34

<h3>${props.title}</h3>

35

<p>${props.content}</p>

36

</div>

37

`;

38

},

39

40

update(context) {

41

// Update logic if needed

42

if (context && context.newContent) {

43

const p = element.querySelector('p');

44

p.textContent = context.newContent;

45

}

46

},

47

48

unmount(keepRootElement) {

49

if (!keepRootElement) {

50

element.innerHTML = '';

51

}

52

}

53

};

54

});

55

56

// Use the pure component

57

const element = document.getElementById("card-container");

58

const cardComponent = createSimpleCard({

59

props: { title: "Pure Card", content: "This is a pure component" }

60

});

61

62

cardComponent.mount(element);

63

```

64

65

## Pure Component Interface

66

67

Pure components implement a simplified interface without the full RiotComponent lifecycle:

68

69

```typescript { .api }

70

interface RiotPureComponent<Context = object> {

71

/** Mount component to DOM element */

72

mount(element: HTMLElement, context?: Context): void;

73

74

/** Update component with new context */

75

update(context?: Context): void;

76

77

/** Unmount component from DOM */

78

unmount(keepRootElement: boolean): void;

79

}

80

```

81

82

### Pure Component Factory Function

83

84

The factory function receives component metadata and returns a pure component instance:

85

86

```typescript { .api }

87

interface PureComponentFactoryFunction<

88

InitialProps extends DefaultProps = DefaultProps,

89

Context = any

90

> {

91

({

92

slots,

93

attributes,

94

props,

95

}: {

96

slots?: TagSlotData<Context>[];

97

attributes?: AttributeExpressionData<Context>[];

98

props?: InitialProps;

99

}): RiotPureComponent<Context>;

100

}

101

```

102

103

## Pure Component Patterns

104

105

### Simple Presentational Component

106

107

```javascript

108

import { pure } from "riot";

109

110

const createButton = pure(({ props, attributes }) => {

111

let element;

112

113

return {

114

mount(el) {

115

element = el;

116

element.innerHTML = `

117

<button class="btn ${props.variant || 'primary'}">

118

${props.label || 'Click me'}

119

</button>

120

`;

121

122

// Add event listeners

123

const button = element.querySelector('button');

124

button.addEventListener('click', props.onClick || (() => {}));

125

},

126

127

update(context) {

128

if (context && context.label) {

129

const button = element.querySelector('button');

130

button.textContent = context.label;

131

}

132

},

133

134

unmount(keepRootElement) {

135

if (!keepRootElement && element) {

136

element.innerHTML = '';

137

}

138

}

139

};

140

});

141

142

// Usage

143

const buttonComponent = createButton({

144

props: {

145

label: "Save",

146

variant: "success",

147

onClick: () => console.log("Saved!")

148

}

149

});

150

```

151

152

### Data Display Component

153

154

```javascript

155

const createDataTable = pure(({ props }) => {

156

let element;

157

158

return {

159

mount(el, context) {

160

element = el;

161

this.render(props.data || []);

162

},

163

164

update(context) {

165

if (context && context.data) {

166

this.render(context.data);

167

}

168

},

169

170

render(data) {

171

const tableHTML = `

172

<table class="data-table">

173

<thead>

174

<tr>${props.columns.map(col => `<th>${col.title}</th>`).join('')}</tr>

175

</thead>

176

<tbody>

177

${data.map(row => `

178

<tr>${props.columns.map(col => `<td>${row[col.key]}</td>`).join('')}</tr>

179

`).join('')}

180

</tbody>

181

</table>

182

`;

183

element.innerHTML = tableHTML;

184

},

185

186

unmount(keepRootElement) {

187

if (!keepRootElement && element) {

188

element.innerHTML = '';

189

}

190

}

191

};

192

});

193

194

// Usage

195

const tableComponent = createDataTable({

196

props: {

197

columns: [

198

{ key: 'name', title: 'Name' },

199

{ key: 'email', title: 'Email' }

200

],

201

data: [

202

{ name: 'Alice', email: 'alice@example.com' },

203

{ name: 'Bob', email: 'bob@example.com' }

204

]

205

}

206

});

207

```

208

209

### Stateful Pure Component

210

211

Even though called "pure", these components can maintain internal state:

212

213

```javascript

214

const createCounter = pure(({ props }) => {

215

let element;

216

let count = props.initial || 0;

217

218

return {

219

mount(el) {

220

element = el;

221

this.render();

222

223

// Add event listeners

224

element.addEventListener('click', (e) => {

225

if (e.target.matches('.increment')) {

226

count++;

227

this.render();

228

} else if (e.target.matches('.decrement')) {

229

count--;

230

this.render();

231

}

232

});

233

},

234

235

update(context) {

236

if (context && typeof context.count === 'number') {

237

count = context.count;

238

this.render();

239

}

240

},

241

242

render() {

243

element.innerHTML = `

244

<div class="counter">

245

<button class="decrement">-</button>

246

<span class="count">${count}</span>

247

<button class="increment">+</button>

248

</div>

249

`;

250

},

251

252

unmount(keepRootElement) {

253

if (!keepRootElement && element) {

254

element.innerHTML = '';

255

}

256

}

257

};

258

});

259

```

260

261

## Pure vs Regular Components

262

263

### When to Use Pure Components

264

265

- **Simple presentational components** without complex lifecycle needs

266

- **Performance-critical scenarios** where you need minimal overhead

267

- **Custom rendering logic** that doesn't fit the standard template system

268

- **Third-party integration** where you need direct DOM control

269

- **Micro-components** with very specific, limited functionality

270

271

### When to Use Regular Components

272

273

- **Complex state management** with lifecycle hooks

274

- **Template-based rendering** with expression bindings

275

- **Parent-child communication** through props and events

276

- **Plugin integration** for cross-cutting concerns

277

- **Standard component patterns** following Riot conventions

278

279

## Types

280

281

```typescript { .api }

282

interface RiotPureComponent<Context = object> {

283

mount(element: HTMLElement, context?: Context): void;

284

update(context?: Context): void;

285

unmount(keepRootElement: boolean): void;

286

}

287

288

interface PureComponentFactoryFunction<

289

InitialProps extends DefaultProps = DefaultProps,

290

Context = any

291

> {

292

({

293

slots,

294

attributes,

295

props,

296

}: {

297

slots?: TagSlotData<Context>[];

298

attributes?: AttributeExpressionData<Context>[];

299

props?: InitialProps;

300

}): RiotPureComponent<Context>;

301

}

302

303

type DefaultProps = Record<PropertyKey, any>;

304

305

type TagSlotData<Context = any> = {

306

id: string;

307

html: string;

308

bindings: BindingData<Context>[];

309

};

310

311

type AttributeExpressionData<Context = any> = {

312

name: string;

313

evaluate: (context?: Context) => any;

314

};

315

```