@tripod311/splash 0.0.15 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +182 -153
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
1
|
# Splash
|
|
2
2
|
|
|
3
|
-
Splash is a zero-dependency
|
|
3
|
+
Splash is a small, zero-dependency UI library built around direct DOM rendering. It aims to provide a component-oriented development experience without introducing a virtual DOM or a separate rendering tree.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
That’s why it’s called **Splash** — just splash your component anywhere on the page and it will work according to its inner logic.
|
|
5
|
+
Each Splash component owns a single root DOM element. Components can be mounted into slots or regular DOM containers, moved between containers, and accessed through their underlying DOM nodes when direct control is useful.
|
|
7
6
|
|
|
8
|
-
Splash
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Splash uses a deliberately compact model: component inputs and local values live in one state object, while templates describe how selected state fields are reflected in the DOM. This approach is designed for small applications and interfaces where a lightweight abstraction and explicit DOM behavior are more useful than a larger framework ecosystem.
|
|
8
|
+
|
|
9
|
+
Splash is also a practical exploration of component lifecycle, selective reactivity, template caching, and direct DOM updates. It is not intended as a drop-in replacement for established production frameworks; it represents a different set of trade-offs tailored to the author's own projects.
|
|
11
10
|
|
|
12
11
|
---
|
|
13
12
|
|
|
14
|
-
## Core
|
|
15
|
-
|
|
16
|
-
1. **Component = one DOM element**
|
|
17
|
-
- No ambiguities, no phantom nodes.
|
|
18
|
-
- Conditional rendering is solved inside the component.
|
|
19
|
-
- If you need a hybrid case — use `ref` and manage it manually.
|
|
20
|
-
|
|
21
|
-
2. **Direct DOM**
|
|
22
|
-
- No virtual tree.
|
|
23
|
-
- Everything is stored and updated directly in the DOM.
|
|
24
|
-
- The browser already optimizes repaints — no need for batching.
|
|
25
|
-
|
|
26
|
-
3. **Minimal directives**
|
|
27
|
-
Just six built-in directives:
|
|
28
|
-
- `data-ref` — reference to a specific element.
|
|
29
|
-
- `data-slot` — mount point for other components.
|
|
30
|
-
- `data-text` — reactive text.
|
|
31
|
-
- `data-html` — dynamic HTML.
|
|
32
|
-
- `data-class` — reactive classes.
|
|
33
|
-
- `data-style` — reactive styles.
|
|
34
|
-
Additionally:
|
|
35
|
-
- `data-prop-*` — reactive binding to any attribute (src, href, etc.).
|
|
36
|
-
|
|
37
|
-
4. **Lifecycle hooks**
|
|
38
|
-
- `mounted()` — called after insertion into the DOM.
|
|
39
|
-
- `unmounted()` — called when removed.
|
|
40
|
-
- `transitionEntry()` — called after mounting + rendering (for CSS animations).
|
|
41
|
-
|
|
42
|
-
5. **Single source of truth: state**
|
|
43
|
-
- Each component receives `options: Record<string, any>`.
|
|
44
|
-
- These turn into the component’s `state`.
|
|
45
|
-
- No props vs. state confusion — only a single object.
|
|
46
|
-
- State updates trigger view updates exactly where needed.
|
|
47
|
-
|
|
48
|
-
6. **Errors are not hidden**
|
|
49
|
-
- Splash is not a nanny.
|
|
50
|
-
- If you create an infinite update loop, that’s your bug.
|
|
51
|
-
- Architectural mistakes are visible immediately.
|
|
13
|
+
## Core ideas
|
|
52
14
|
|
|
53
|
-
|
|
15
|
+
### One root element per component
|
|
16
|
+
|
|
17
|
+
Every component owns one root DOM element. This gives the component a clear position in the document and allows it to be mounted, moved, or removed as a single unit.
|
|
18
|
+
|
|
19
|
+
Conditional content is normally handled through slots. When lower-level control is needed, elements can also be accessed through refs or the component's root node.
|
|
20
|
+
|
|
21
|
+
### Direct DOM rendering
|
|
22
|
+
|
|
23
|
+
Splash stores and updates the rendered view directly in the DOM. It does not maintain a virtual representation or perform reconciliation.
|
|
24
|
+
|
|
25
|
+
Reactive directives update only the elements bound to the state field that changed. Splash does not add its own scheduling layer, so update timing remains explicit and follows normal browser DOM behavior.
|
|
26
|
+
|
|
27
|
+
### A small directive set
|
|
28
|
+
|
|
29
|
+
Splash provides a limited set of template directives:
|
|
30
|
+
|
|
31
|
+
- `data-ref` — stores a reference to an element.
|
|
32
|
+
- `data-slot` — declares a mount point for child components.
|
|
33
|
+
- `data-text` — binds a state value to `innerText`.
|
|
34
|
+
- `data-html` — binds a state value to `innerHTML`.
|
|
35
|
+
- `data-class` — binds an array of CSS classes.
|
|
36
|
+
- `data-style` — binds an object of inline styles.
|
|
37
|
+
- `data-prop-*` — binds a value to an HTML attribute such as `src` or `href`.
|
|
38
|
+
|
|
39
|
+
Directive values are identifiers, not JavaScript expressions. Application logic remains in the component class.
|
|
40
|
+
|
|
41
|
+
### Component lifecycle
|
|
42
|
+
|
|
43
|
+
Components can respond to three lifecycle stages:
|
|
44
|
+
|
|
45
|
+
- `mounted()` — called after the component is inserted into the DOM.
|
|
46
|
+
- `transitionReady()` — called after mounting and initial rendering, allowing CSS transitions to begin.
|
|
47
|
+
- `unmounted()` — called when the component is unmounted.
|
|
54
48
|
|
|
55
|
-
|
|
49
|
+
### One component state object
|
|
56
50
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
Each component receives an `options: Record<string, any>` object, which is used to initialize its state. Splash does not create separate props and local-state systems.
|
|
52
|
+
|
|
53
|
+
State fields can be updated individually or as a group. Only directives bound to changed fields are updated.
|
|
54
|
+
|
|
55
|
+
### Explicit behavior
|
|
56
|
+
|
|
57
|
+
Splash keeps its update model intentionally small. State changes, DOM access, and component relationships remain visible in application code. The library provides lifecycle and rendering primitives while leaving broader architecture decisions to the application.
|
|
61
58
|
|
|
62
59
|
---
|
|
63
60
|
|
|
64
61
|
## API
|
|
65
62
|
|
|
66
|
-
### 1.
|
|
63
|
+
### 1. Components
|
|
64
|
+
|
|
65
|
+
A component combines a template, a state object, lifecycle hooks, and one root DOM element. The root is available through `component.DOMNode` when direct DOM access is required.
|
|
67
66
|
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
A component template is regular HTML with optional Splash directives.
|
|
68
|
+
|
|
69
|
+
**Example template (`myComponent.html`):**
|
|
70
70
|
|
|
71
|
-
**Example template (myComponent.html):**
|
|
72
71
|
```html
|
|
73
72
|
<div data-ref="container">
|
|
74
73
|
<h1 data-text="header-text"></h1>
|
|
75
74
|
<p>Static paragraph</p>
|
|
76
75
|
<!--slot:conditionalPart-->
|
|
77
|
-
<p data-style="conclusion-style">This is
|
|
76
|
+
<p data-style="conclusion-style">This is the final paragraph.</p>
|
|
78
77
|
</div>
|
|
79
78
|
```
|
|
80
79
|
|
|
81
|
-
**Component definition (myComponent.ts):**
|
|
80
|
+
**Component definition (`myComponent.ts`):**
|
|
81
|
+
|
|
82
82
|
```ts
|
|
83
|
-
import { Component } from "@tripod311/splash"
|
|
84
|
-
import View from "./myComponent.html?raw"
|
|
85
|
-
import ChildComponent from "./childComponent.js"
|
|
83
|
+
import { Component } from "@tripod311/splash";
|
|
84
|
+
import View from "./myComponent.html?raw";
|
|
85
|
+
import ChildComponent from "./childComponent.js";
|
|
86
86
|
|
|
87
87
|
export default class MyComponent extends Component {
|
|
88
88
|
private static componentName: string = "MyComponent";
|
|
@@ -91,30 +91,32 @@ export default class MyComponent extends Component {
|
|
|
91
91
|
constructor (options: Record<string, any>) {
|
|
92
92
|
super(options);
|
|
93
93
|
|
|
94
|
-
//
|
|
94
|
+
// Update one state field.
|
|
95
95
|
this.state.setProp("header-text", "My component title");
|
|
96
96
|
|
|
97
|
-
//
|
|
97
|
+
// Observe a state field.
|
|
98
98
|
this.state.on("src", (newValue: any, oldValue: any) => {
|
|
99
|
-
|
|
99
|
+
// Component-specific logic.
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
//
|
|
102
|
+
// Update several fields together.
|
|
103
103
|
this.state.update({
|
|
104
104
|
diffVar1: "someValue",
|
|
105
105
|
diffVar2: "someOtherValue"
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
//
|
|
109
|
-
this.slots
|
|
108
|
+
// Add a child component to a slot.
|
|
109
|
+
this.slots.conditionalPart.push(
|
|
110
|
+
new ChildComponent({ var1: 1 })
|
|
111
|
+
);
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
mounted () {
|
|
113
115
|
super.mounted();
|
|
114
116
|
}
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
+
transitionReady () {
|
|
119
|
+
// The component is mounted and ready for CSS transitions.
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
unmounted () {
|
|
@@ -123,60 +125,63 @@ export default class MyComponent extends Component {
|
|
|
123
125
|
}
|
|
124
126
|
```
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
Each component class must have a distinct `componentName`. Splash uses this name as the key for its shared template cache.
|
|
129
|
+
|
|
130
|
+
#### Component events
|
|
131
|
+
|
|
132
|
+
Components can emit and subscribe to application-level events:
|
|
133
|
+
|
|
127
134
|
```ts
|
|
128
|
-
// Child
|
|
135
|
+
// Child component
|
|
129
136
|
this.emit("click", { some: "payload" });
|
|
130
137
|
|
|
131
|
-
// Parent
|
|
138
|
+
// Parent component
|
|
132
139
|
childComponent.on("click", payload => {
|
|
133
140
|
console.log(payload);
|
|
134
141
|
});
|
|
135
142
|
```
|
|
136
143
|
|
|
137
|
-
|
|
144
|
+
#### Updating a child component
|
|
145
|
+
|
|
138
146
|
```ts
|
|
139
|
-
childComponent.update({
|
|
147
|
+
childComponent.update({
|
|
148
|
+
color: "newColor"
|
|
149
|
+
});
|
|
140
150
|
```
|
|
141
151
|
|
|
142
|
-
⚠️ Note: Each component must have a distinct `componentName`.
|
|
143
|
-
Splash caches and shares templates between components with the same names.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
152
|
### 2. Slots
|
|
148
153
|
|
|
149
|
-
Slots
|
|
154
|
+
Slots are ordered collections of child components associated with mount points in a template. They manage insertion, removal, and the corresponding component lifecycle calls.
|
|
150
155
|
|
|
151
|
-
**Example:**
|
|
152
156
|
```ts
|
|
153
|
-
const child = new ChildComponent({
|
|
154
|
-
|
|
157
|
+
const child = new ChildComponent({
|
|
158
|
+
title: "Child component"
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
this.slots.mySlot.setContent([child]);
|
|
155
162
|
```
|
|
156
163
|
|
|
157
|
-
|
|
158
|
-
- `setContent(Component[])` — replace slot contents.
|
|
159
|
-
- `clear(): Component[]` — unmount all and return them.
|
|
160
|
-
- `push(c: Component)` — append component.
|
|
161
|
-
- `pop(): Component | undefined` — remove last.
|
|
162
|
-
- `unshift(c: Component)` — prepend component.
|
|
163
|
-
- `shift(): Component | undefined` — remove first.
|
|
164
|
-
- `inject(pos: number, c: Component)` — insert at position.
|
|
165
|
-
- `remove(pos: number): Component | undefined` — remove at position.
|
|
166
|
-
- `getByIndex(index: number): Component | undefined` — access without unmounting.
|
|
167
|
-
- `length: number` — number of components in slot.
|
|
164
|
+
Slot API:
|
|
168
165
|
|
|
169
|
-
|
|
166
|
+
- `setContent(components: Component[])` — replaces the current contents.
|
|
167
|
+
- `clear(): Component[]` — unmounts and returns all components.
|
|
168
|
+
- `push(component: Component)` — appends a component.
|
|
169
|
+
- `pop(): Component | undefined` — removes the last component.
|
|
170
|
+
- `unshift(component: Component)` — prepends a component.
|
|
171
|
+
- `shift(): Component | undefined` — removes the first component.
|
|
172
|
+
- `inject(position: number, component: Component)` — inserts a component at a specified position.
|
|
173
|
+
- `remove(position: number): Component | undefined` — removes a component at a specified position.
|
|
174
|
+
- `getByIndex(index: number): Component | undefined` — returns a component without removing it.
|
|
175
|
+
- `length: number` — returns the number of components in the slot.
|
|
170
176
|
|
|
171
177
|
### 3. Drops
|
|
172
178
|
|
|
173
|
-
Drops are lightweight HTML
|
|
174
|
-
They are not components: no state, no lifecycle, no reactivity.
|
|
175
|
-
Instead, they allow you to register small reusable pieces of HTML with the same directives (`data-ref`, `data-text`, `data-html`, `data-class`, `data-style`, `data-prop-*`).
|
|
179
|
+
Drops are lightweight reusable HTML fragments stored in `TemplateCache`. They support the same element-binding directives as component templates but do not have component state, events, or lifecycle hooks.
|
|
176
180
|
|
|
177
|
-
A drop can be
|
|
181
|
+
A drop can be created, populated with initial values, and inserted into the DOM as a regular node.
|
|
182
|
+
|
|
183
|
+
#### Registering a drop
|
|
178
184
|
|
|
179
|
-
**Example template registration:**
|
|
180
185
|
```ts
|
|
181
186
|
TemplateCache.registerDrop("chatMessage", `
|
|
182
187
|
<div class="msg">
|
|
@@ -186,122 +191,146 @@ TemplateCache.registerDrop("chatMessage", `
|
|
|
186
191
|
`);
|
|
187
192
|
```
|
|
188
193
|
|
|
189
|
-
|
|
194
|
+
#### Creating a drop
|
|
195
|
+
|
|
196
|
+
The second argument is optional and can be used to populate the drop's directives:
|
|
197
|
+
|
|
190
198
|
```ts
|
|
191
|
-
// second parameter is optional, use it when you want to fill drop with some content.
|
|
192
199
|
const drop = TemplateCache.createDrop("chatMessage", {
|
|
193
200
|
author: "Alice",
|
|
194
201
|
text: "<b>Hello!</b>"
|
|
195
202
|
});
|
|
196
203
|
|
|
197
|
-
|
|
198
|
-
console.log(drop.refs.author.innerText); // "Alice"
|
|
204
|
+
console.log(drop.refs.author.innerText);
|
|
199
205
|
|
|
200
|
-
// Insert into DOM
|
|
201
206
|
document.body.appendChild(drop.node);
|
|
202
207
|
```
|
|
203
208
|
|
|
204
|
-
|
|
209
|
+
Drop interface:
|
|
210
|
+
|
|
205
211
|
```ts
|
|
206
212
|
export interface Drop {
|
|
207
|
-
node: Node;
|
|
208
|
-
refs: Record<string, HTMLElement>;
|
|
213
|
+
node: Node;
|
|
214
|
+
refs: Record<string, HTMLElement>;
|
|
209
215
|
}
|
|
210
216
|
```
|
|
211
217
|
|
|
212
|
-
|
|
218
|
+
Values passed to `data-html` are assigned through `innerHTML`. Do not use unsanitized user-provided content with this directive.
|
|
219
|
+
|
|
220
|
+
### 4. Generic components
|
|
213
221
|
|
|
214
|
-
Generic components
|
|
215
|
-
They are useful when you need something that behaves like a component (can be mounted into a slot, updated, unmounted), but is too simple to justify creating a separate `.ts` + `.html` pair.
|
|
222
|
+
Generic components provide component lifecycle and slot compatibility without requiring a dedicated component class and template file.
|
|
216
223
|
|
|
217
|
-
|
|
224
|
+
They are useful when a slot may contain either a regular component or a small piece of interface such as an empty-state or error message.
|
|
218
225
|
|
|
219
|
-
**Example:**
|
|
220
226
|
```ts
|
|
221
227
|
const result = await someAsyncRequest();
|
|
222
228
|
|
|
223
229
|
if (!result.error) {
|
|
224
|
-
this.slots.content.push(
|
|
230
|
+
this.slots.content.push(
|
|
231
|
+
new MyRegularComponent({})
|
|
232
|
+
);
|
|
225
233
|
} else {
|
|
226
|
-
// Wrap a drop (or any HTML node) into a generic component
|
|
227
234
|
const errorDrop = TemplateCache.createDrop("errorMessage");
|
|
228
|
-
|
|
235
|
+
|
|
236
|
+
this.slots.content.push(
|
|
237
|
+
Component.generic(
|
|
238
|
+
{ text: result.details },
|
|
239
|
+
errorDrop.node
|
|
240
|
+
)
|
|
241
|
+
);
|
|
229
242
|
}
|
|
230
243
|
```
|
|
231
244
|
|
|
232
|
-
|
|
233
|
-
This keeps your slot API consistent — you always mount components, whether they are full, generic, or lightweight wrappers around static HTML.
|
|
245
|
+
This keeps the slot API consistent: every slot contains components, while simple DOM fragments can be wrapped only when lifecycle-aware mounting is needed.
|
|
234
246
|
|
|
235
|
-
|
|
247
|
+
### 5. Mounting and unmounting
|
|
236
248
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
To place a component on the page you can use the `mount` method:
|
|
249
|
+
Use `mount()` to append a component to a DOM container:
|
|
240
250
|
|
|
241
251
|
```ts
|
|
242
252
|
import MyComponent from "./MyComponent.js";
|
|
243
253
|
|
|
244
|
-
const app = new MyComponent({
|
|
254
|
+
const app = new MyComponent({
|
|
255
|
+
title: "Hello Splash!"
|
|
256
|
+
});
|
|
257
|
+
|
|
245
258
|
app.mount(document.body);
|
|
246
259
|
```
|
|
247
260
|
|
|
248
|
-
|
|
261
|
+
Mounting inserts the component's root node and calls its `mounted()` lifecycle hook.
|
|
249
262
|
|
|
250
|
-
|
|
263
|
+
Use `unmount()` to remove it:
|
|
251
264
|
|
|
252
265
|
```ts
|
|
253
266
|
app.unmount();
|
|
254
267
|
```
|
|
255
268
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
⚠️ **Important:**
|
|
259
|
-
Unmounting a component does **not** destroy its state or DOM nodes.
|
|
260
|
-
The component can be mounted again into another container without losing any data:
|
|
269
|
+
Unmounting does not destroy the component's state or DOM tree. The same instance can later be mounted into another container:
|
|
261
270
|
|
|
262
271
|
```ts
|
|
263
|
-
// move component from one place to another
|
|
264
272
|
app.unmount();
|
|
265
|
-
app.mount(
|
|
273
|
+
app.mount(
|
|
274
|
+
document.getElementById("new-container")!
|
|
275
|
+
);
|
|
266
276
|
```
|
|
267
277
|
|
|
268
|
-
This behavior
|
|
278
|
+
This behavior is useful for interfaces such as modal systems, tabs, movable panels, and window managers.
|
|
279
|
+
|
|
280
|
+
When lifecycle hooks matter, prefer `mount()`, `unmount()`, and slots over manipulating `component.DOMNode` directly.
|
|
269
281
|
|
|
270
282
|
---
|
|
271
283
|
|
|
272
|
-
## Template
|
|
284
|
+
## Template directives
|
|
285
|
+
|
|
286
|
+
Directive values are string identifiers. Splash binds them to state fields but does not evaluate them as expressions.
|
|
287
|
+
|
|
288
|
+
- `data-ref` — exposes an element through `this.refs`.
|
|
289
|
+
- `data-text` — assigns a value through `innerText`.
|
|
290
|
+
- `data-html` — assigns a value through `innerHTML`.
|
|
291
|
+
- `data-class` — applies an array of CSS class names.
|
|
292
|
+
- `data-style` — applies an object in the form `{ [property]: value }`.
|
|
293
|
+
- `data-prop-*` — binds another HTML attribute, such as `data-prop-src` or `data-prop-href`.
|
|
273
294
|
|
|
274
|
-
|
|
275
|
-
Initial HTML values are used to initialize state.
|
|
295
|
+
Initial directive values declared in the template are used to initialize the corresponding state fields.
|
|
276
296
|
|
|
277
|
-
|
|
278
|
-
- `data-text` — reactive `innerText`.
|
|
279
|
-
- `data-html` — reactive `innerHTML`.
|
|
280
|
-
- `data-class` — array of strings for CSS classes.
|
|
281
|
-
- `data-style` — object `{ [prop]: value }` for styles.
|
|
282
|
-
- `data-prop-*` — any other attribute (e.g. `data-prop-src` for `<img>`).
|
|
297
|
+
The current value of a reactive field can be read with:
|
|
283
298
|
|
|
284
|
-
Current reactive values can be accessed via:
|
|
285
299
|
```ts
|
|
286
|
-
this.state.getProp("reactive-
|
|
300
|
+
this.state.getProp("reactive-variable-name");
|
|
287
301
|
```
|
|
288
302
|
|
|
289
303
|
---
|
|
290
304
|
|
|
291
|
-
##
|
|
305
|
+
## Design choices
|
|
292
306
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
307
|
+
Splash differs from virtual-DOM and compiler-based UI frameworks in several deliberate ways:
|
|
308
|
+
|
|
309
|
+
- The DOM is the only rendered tree.
|
|
310
|
+
- Each component corresponds to one root DOM element.
|
|
311
|
+
- Reactivity is opt-in through template directives.
|
|
312
|
+
- Component inputs and local values share one state interface.
|
|
313
|
+
- DOM access remains available when an application needs it.
|
|
314
|
+
- Update scheduling and broader application architecture remain under developer control.
|
|
315
|
+
|
|
316
|
+
These choices reduce the amount of machinery between component code and the browser, while placing more responsibility on the application to manage update patterns and DOM interactions carefully.
|
|
298
317
|
|
|
299
318
|
---
|
|
300
319
|
|
|
301
|
-
## Example
|
|
320
|
+
## Example scenarios
|
|
302
321
|
|
|
303
|
-
|
|
304
|
-
- **Mount animations:** use `transitionEntry()` for CSS transitions.
|
|
305
|
-
- **Conditional rendering:** mount/unmount via `data-slot`, no `v-if` or diffing logic.
|
|
322
|
+
### Forms
|
|
306
323
|
|
|
307
|
-
|
|
324
|
+
Use `data-ref` to access native form controls and attach regular DOM event listeners. Splash does not introduce a separate form model.
|
|
325
|
+
|
|
326
|
+
### Mount transitions
|
|
327
|
+
|
|
328
|
+
Use `transitionReady()` to apply classes or styles after the component has been mounted and initially rendered.
|
|
329
|
+
|
|
330
|
+
### Conditional content
|
|
331
|
+
|
|
332
|
+
Use slots to add, replace, or remove child components while preserving their lifecycle behavior.
|
|
333
|
+
|
|
334
|
+
### Direct DOM integration
|
|
335
|
+
|
|
336
|
+
Use refs or `component.DOMNode` when integrating browser APIs or third-party code that expects regular DOM elements.
|