or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asset-resolution.mdbuiltin-components.mdcomponents.mdcomposition-helpers.mddependency-injection.mderror-handling.mdhydration.mdindex.mdinternal-render-helpers.mdlifecycle.mdreactivity.mdscheduler-timing.mdssr-context.mdvdom-rendering.mdwatch-effects.md

composition-helpers.mddocs/

0

# Composition Helpers

1

2

Vue provides helper utilities for composition API usage including template refs, v-model integration, unique ID generation, and setup context access.

3

4

## Capabilities

5

6

### Template References

7

8

Create reactive references to template elements and component instances.

9

10

```typescript { .api }

11

/**

12

* Creates a template ref for accessing DOM elements or component instances

13

* @param key - Template ref key (must match ref attribute in template)

14

* @returns Template ref object

15

*/

16

function useTemplateRef<T = any>(key: string): TemplateRef<T>;

17

18

interface TemplateRef<T = any> extends Ref<T> {

19

readonly value: T;

20

}

21

```

22

23

**Usage Examples:**

24

25

```typescript

26

import { defineComponent, useTemplateRef, onMounted } from "@vue/runtime-core";

27

28

const MyComponent = defineComponent({

29

setup() {

30

// Create template refs

31

const inputRef = useTemplateRef<HTMLInputElement>('input');

32

const childRef = useTemplateRef<ComponentInstance>('child');

33

34

onMounted(() => {

35

// Access DOM element

36

if (inputRef.value) {

37

inputRef.value.focus();

38

console.log('Input value:', inputRef.value.value);

39

}

40

41

// Access child component instance

42

if (childRef.value) {

43

// Call child component methods

44

childRef.value.someMethod();

45

}

46

});

47

48

return {

49

inputRef,

50

childRef

51

};

52

},

53

54

template: `

55

<div>

56

<input ref="input" type="text" />

57

<ChildComponent ref="child" />

58

</div>

59

`

60

});

61

```

62

63

### Model References

64

65

Create reactive references for v-model bindings with built-in getter/setter logic.

66

67

```typescript { .api }

68

/**

69

* Creates a model ref for v-model integration

70

* @param props - Component props object

71

* @param name - Model property name

72

* @param options - Model configuration options

73

* @returns Model ref with getter/setter

74

*/

75

function useModel<T>(

76

props: Record<string, any>,

77

name: string,

78

options?: UseModelOptions

79

): ModelRef<T>;

80

81

interface UseModelOptions {

82

/**

83

* Default value when prop is undefined

84

*/

85

defaultValue?: any;

86

87

/**

88

* Whether the model should be deeply reactive

89

*/

90

deep?: boolean;

91

92

/**

93

* Transform function for incoming value

94

*/

95

get?(value: any): any;

96

97

/**

98

* Transform function for outgoing value

99

*/

100

set?(value: any): any;

101

}

102

103

interface ModelRef<T> extends Ref<T> {

104

readonly [ModelRefMarkerSymbol]: true;

105

}

106

```

107

108

**Usage Examples:**

109

110

```typescript

111

import { defineComponent, useModel } from "@vue/runtime-core";

112

113

// Basic v-model component

114

const CustomInput = defineComponent({

115

props: {

116

modelValue: String,

117

placeholder: String

118

},

119

emits: ['update:modelValue'],

120

121

setup(props, { emit }) {

122

// Create model ref that syncs with v-model

123

const model = useModel(props, 'modelValue');

124

125

return {

126

model

127

};

128

},

129

130

template: `

131

<input

132

v-model="model"

133

:placeholder="placeholder"

134

/>

135

`

136

});

137

138

// Multiple v-model support

139

const MultiModelComponent = defineComponent({

140

props: {

141

firstName: String,

142

lastName: String,

143

age: Number

144

},

145

emits: ['update:firstName', 'update:lastName', 'update:age'],

146

147

setup(props) {

148

const firstName = useModel(props, 'firstName');

149

const lastName = useModel(props, 'lastName');

150

const age = useModel(props, 'age', {

151

defaultValue: 0,

152

get: (value) => Number(value) || 0,

153

set: (value) => Math.max(0, Math.floor(value))

154

});

155

156

return {

157

firstName,

158

lastName,

159

age

160

};

161

}

162

});

163

164

// Usage:

165

// <MultiModelComponent

166

// v-model:firstName="user.firstName"

167

// v-model:lastName="user.lastName"

168

// v-model:age="user.age"

169

// />

170

```

171

172

### Unique ID Generation

173

174

Generate unique IDs for accessibility and form associations.

175

176

```typescript { .api }

177

/**

178

* Generates a unique ID for the current component instance

179

* @returns Unique string ID

180

*/

181

function useId(): string;

182

```

183

184

**Usage Examples:**

185

186

```typescript

187

import { defineComponent, useId } from "@vue/runtime-core";

188

189

const FormField = defineComponent({

190

props: {

191

label: String,

192

required: Boolean

193

},

194

195

setup(props) {

196

const id = useId();

197

198

return {

199

id,

200

inputId: `input-${id}`,

201

labelId: `label-${id}`,

202

errorId: `error-${id}`

203

};

204

},

205

206

template: `

207

<div>

208

<label :id="labelId" :for="inputId">

209

{{ label }}

210

<span v-if="required" aria-label="required">*</span>

211

</label>

212

<input

213

:id="inputId"

214

:aria-labelledby="labelId"

215

:aria-describedby="errorId"

216

:required="required"

217

/>

218

<div :id="errorId" class="error" aria-live="polite">

219

<!-- Error messages -->

220

</div>

221

</div>

222

`

223

});

224

225

// Multiple IDs in one component

226

const MultiIdComponent = defineComponent({

227

setup() {

228

const formId = useId();

229

const modalId = useId();

230

const tabsId = useId();

231

232

return {

233

formId: `form-${formId}`,

234

modalId: `modal-${modalId}`,

235

tabsId: `tabs-${tabsId}`

236

};

237

}

238

});

239

```

240

241

### Setup Context Access

242

243

Access component slots and attributes within setup function.

244

245

```typescript { .api }

246

/**

247

* Gets setup context slots

248

* @returns Component slots object

249

*/

250

function useSlots(): Slots;

251

252

/**

253

* Gets setup context attrs (non-prop attributes)

254

* @returns Component attrs object

255

*/

256

function useAttrs(): Data;

257

258

interface Slots {

259

[name: string]: Slot | undefined;

260

default?: Slot;

261

}

262

263

type Slot<T = any> = (...args: any[]) => VNode[];

264

265

type Data = Record<string, unknown>;

266

```

267

268

**Usage Examples:**

269

270

```typescript

271

import { defineComponent, useSlots, useAttrs, h } from "@vue/runtime-core";

272

273

const WrapperComponent = defineComponent({

274

setup() {

275

const slots = useSlots();

276

const attrs = useAttrs();

277

278

// Check available slots

279

const hasHeader = !!slots.header;

280

const hasFooter = !!slots.footer;

281

const hasDefault = !!slots.default;

282

283

console.log('Available slots:', Object.keys(slots));

284

console.log('Component attrs:', attrs);

285

286

return () => h('div', {

287

class: 'wrapper',

288

...attrs // Forward all non-prop attributes

289

}, [

290

hasHeader && h('header', { class: 'header' }, slots.header!()),

291

hasDefault && h('main', { class: 'content' }, slots.default!()),

292

hasFooter && h('footer', { class: 'footer' }, slots.footer!())

293

]);

294

}

295

});

296

297

// Dynamic slot rendering

298

const DynamicSlots = defineComponent({

299

setup() {

300

const slots = useSlots();

301

302

return () => {

303

const slotElements = Object.entries(slots).map(([name, slot]) => {

304

if (slot) {

305

return h('div', {

306

key: name,

307

class: `slot-${name}`

308

}, slot());

309

}

310

return null;

311

}).filter(Boolean);

312

313

return h('div', { class: 'dynamic-slots' }, slotElements);

314

};

315

}

316

});

317

318

// Attrs manipulation

319

const AttrsComponent = defineComponent({

320

props: {

321

title: String

322

},

323

324

setup(props) {

325

const attrs = useAttrs();

326

327

// Filter out certain attributes

328

const filteredAttrs = computed(() => {

329

const { class: className, style, ...rest } = attrs;

330

return rest;

331

});

332

333

// Combine with custom attributes

334

const combinedAttrs = computed(() => ({

335

...filteredAttrs.value,

336

'data-component': 'attrs-component',

337

'aria-label': props.title || 'Custom component'

338

}));

339

340

return {

341

combinedAttrs

342

};

343

}

344

});

345

```

346

347

### Advanced Patterns

348

349

```typescript

350

// Composable using multiple helpers

351

function useFormField(name: string) {

352

const id = useId();

353

const attrs = useAttrs();

354

355

const fieldId = `field-${id}`;

356

const labelId = `label-${id}`;

357

const errorId = `error-${id}`;

358

359

// Extract validation attributes

360

const validationAttrs = computed(() => {

361

const { required, minlength, maxlength, pattern } = attrs;

362

return { required, minlength, maxlength, pattern };

363

});

364

365

return {

366

fieldId,

367

labelId,

368

errorId,

369

validationAttrs

370

};

371

}

372

373

// Ref forwarding pattern

374

const ForwardRefComponent = defineComponent({

375

setup(_, { expose }) {

376

const elementRef = useTemplateRef<HTMLElement>('element');

377

378

// Expose ref to parent

379

expose({

380

focus: () => elementRef.value?.focus(),

381

blur: () => elementRef.value?.blur(),

382

element: elementRef

383

});

384

385

return {

386

elementRef

387

};

388

}

389

});

390

```

391

392

## Types

393

394

```typescript { .api }

395

interface TemplateRef<T = any> extends Ref<T> {

396

/**

397

* Template ref marker for type discrimination

398

*/

399

readonly [TemplateRefSymbol]: true;

400

}

401

402

interface ModelRef<T = any> extends Ref<T> {

403

/**

404

* Model ref marker for type discrimination

405

*/

406

readonly [ModelRefMarkerSymbol]: true;

407

}

408

409

type UseModelOptions = {

410

defaultValue?: unknown;

411

deep?: boolean;

412

get?(value: unknown): unknown;

413

set?(value: unknown): unknown;

414

};

415

416

interface Slots {

417

[name: string]: Slot | undefined;

418

}

419

420

type Slot<T extends Record<string, any> = any> = (

421

props: T

422

) => VNode[];

423

424

type Data = Record<string, unknown>;

425

```