0
# Custom Injection
1
2
Advanced customization of CSS injection behavior with custom JavaScript code and runtime functions.
3
4
## Capabilities
5
6
### Custom Injection Types
7
8
Type definitions for custom CSS injection functions.
9
10
```typescript { .api }
11
/**
12
* Function that returns JavaScript code string for CSS injection
13
* @param cssCode - CSS code to inject (as string literal)
14
* @param options - Injection configuration options
15
* @returns JavaScript code string that will inject the CSS
16
*/
17
type InjectCode = (cssCode: string, options: InjectCodeOptions) => string;
18
19
/**
20
* Runtime function that directly injects CSS into the DOM
21
* @param cssCode - CSS code to inject
22
* @param options - Injection configuration options
23
*/
24
type InjectCodeFunction = (cssCode: string, options: InjectCodeOptions) => void;
25
26
/**
27
* Options passed to injection functions
28
*/
29
interface InjectCodeOptions {
30
/** Style element ID or generator function */
31
styleId?: string | (() => string);
32
/** Enable strict CSP support with nonce */
33
useStrictCSP?: boolean;
34
/** Additional attributes for the style element */
35
attributes?: { [key: string]: string } | undefined;
36
}
37
```
38
39
### Custom Injection Code
40
41
Provide a function that returns JavaScript code for CSS injection.
42
43
```typescript { .api }
44
/**
45
* Custom CSS injection code function
46
* Returns JavaScript code that will be executed to inject CSS
47
*/
48
injectCode?: InjectCode;
49
```
50
51
**Usage Examples:**
52
53
```typescript
54
import { defineConfig } from "vite";
55
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
56
57
// Basic custom injection code
58
export default defineConfig({
59
plugins: [
60
cssInjectedByJsPlugin({
61
injectCode: (cssCode, options) => {
62
return `
63
try {
64
if (typeof document !== 'undefined') {
65
var style = document.createElement('style');
66
${options.styleId ? `style.id = '${options.styleId}';` : ''}
67
style.appendChild(document.createTextNode(${cssCode}));
68
document.head.appendChild(style);
69
}
70
} catch (e) {
71
console.error('CSS injection failed:', e);
72
}
73
`;
74
},
75
}),
76
],
77
});
78
79
// Advanced injection with error handling and CSP
80
export default defineConfig({
81
plugins: [
82
cssInjectedByJsPlugin({
83
injectCode: (cssCode, options) => {
84
let attributeCode = '';
85
86
// Add style ID if provided
87
if (options.styleId) {
88
attributeCode += `style.id = '${options.styleId}';`;
89
}
90
91
// Add CSP nonce if enabled
92
if (options.useStrictCSP) {
93
attributeCode += `
94
var nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
95
if (nonceMeta) style.nonce = nonceMeta.content;
96
`;
97
}
98
99
// Add custom attributes
100
if (options.attributes) {
101
for (const [key, value] of Object.entries(options.attributes)) {
102
attributeCode += `style.setAttribute('${key}', '${value}');`;
103
}
104
}
105
106
return `
107
try {
108
if (typeof document !== 'undefined') {
109
var style = document.createElement('style');
110
${attributeCode}
111
style.appendChild(document.createTextNode(${cssCode}));
112
document.head.appendChild(style);
113
}
114
} catch (e) {
115
console.error('vite-plugin-css-injected-by-js injection failed:', e);
116
}
117
`;
118
},
119
}),
120
],
121
});
122
```
123
124
### Custom Injection Runtime Function
125
126
Provide a runtime function that directly injects CSS into the DOM.
127
128
```typescript { .api }
129
/**
130
* Custom CSS injection runtime function
131
* Executed directly at runtime to inject CSS
132
*/
133
injectCodeFunction?: InjectCodeFunction;
134
```
135
136
**Usage Examples:**
137
138
```typescript
139
// Basic runtime injection function
140
export default defineConfig({
141
plugins: [
142
cssInjectedByJsPlugin({
143
injectCodeFunction: (cssCode, options) => {
144
try {
145
if (typeof document !== 'undefined') {
146
const style = document.createElement('style');
147
148
// Set style ID if provided
149
if (options.styleId) {
150
style.id = options.styleId;
151
}
152
153
// Set CSP nonce if enabled
154
if (options.useStrictCSP) {
155
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
156
if (nonceMeta) {
157
style.nonce = nonceMeta.content;
158
}
159
}
160
161
// Set custom attributes
162
if (options.attributes) {
163
for (const [key, value] of Object.entries(options.attributes)) {
164
style.setAttribute(key, value);
165
}
166
}
167
168
style.appendChild(document.createTextNode(cssCode));
169
document.head.appendChild(style);
170
}
171
} catch (error) {
172
console.error('CSS injection failed:', error);
173
}
174
},
175
}),
176
],
177
});
178
179
// Advanced runtime function with insertion control
180
export default defineConfig({
181
plugins: [
182
cssInjectedByJsPlugin({
183
injectCodeFunction: (cssCode, options) => {
184
try {
185
if (typeof document !== 'undefined') {
186
const style = document.createElement('style');
187
188
// Configure style element
189
if (options.styleId) {
190
style.id = options.styleId;
191
}
192
193
// Add all custom attributes
194
if (options.attributes) {
195
Object.entries(options.attributes).forEach(([key, value]) => {
196
style.setAttribute(key, value);
197
});
198
}
199
200
// Handle CSP nonce
201
if (options.useStrictCSP) {
202
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
203
if (nonceMeta?.content) {
204
style.nonce = nonceMeta.content;
205
}
206
}
207
208
// Insert CSS content
209
style.appendChild(document.createTextNode(cssCode));
210
211
// Insert at specific position (after title if exists)
212
const title = document.head.querySelector('title');
213
if (title) {
214
document.head.insertBefore(style, title.nextSibling);
215
} else {
216
document.head.appendChild(style);
217
}
218
}
219
} catch (error) {
220
console.error('vite-plugin-css-injected-by-js:', error);
221
}
222
},
223
}),
224
],
225
});
226
```
227
228
### Injection Options
229
230
Configuration options passed to custom injection functions.
231
232
```typescript { .api }
233
/**
234
* Options passed to injection functions
235
*/
236
interface InjectCodeOptions {
237
/** Style element ID or generator function */
238
styleId?: string | (() => string);
239
/** Enable strict CSP support with nonce */
240
useStrictCSP?: boolean;
241
/** Additional attributes for the style element */
242
attributes?: { [key: string]: string } | undefined;
243
}
244
```
245
246
**Option Details:**
247
248
- **`styleId`**: Sets the `id` attribute on the created style element
249
- **`useStrictCSP`**: Enables Content Security Policy support by reading nonce from meta tag
250
- **`attributes`**: Additional attributes to set on the style element (includes development mode attributes)
251
252
### Development Mode Integration
253
254
Custom injection functions work with development mode and receive additional attributes:
255
256
```typescript
257
// Development mode aware injection
258
injectCodeFunction: (cssCode, options) => {
259
try {
260
if (typeof document !== 'undefined') {
261
const style = document.createElement('style');
262
263
// Handle all attributes (including dev mode attributes)
264
if (options.attributes) {
265
Object.entries(options.attributes).forEach(([key, value]) => {
266
style.setAttribute(key, value);
267
});
268
}
269
270
// In development mode, attributes will include:
271
// { "data-vite-dev-id": "path/to/file.css" }
272
273
style.appendChild(document.createTextNode(cssCode));
274
document.head.appendChild(style);
275
}
276
} catch (error) {
277
console.error('CSS injection failed:', error);
278
}
279
}
280
```
281
282
### Content Security Policy Support
283
284
When `useStrictCSP` is enabled, injection functions should read the nonce from a meta tag:
285
286
```typescript
287
// CSP-compliant injection
288
injectCodeFunction: (cssCode, options) => {
289
try {
290
if (typeof document !== 'undefined') {
291
const style = document.createElement('style');
292
293
if (options.useStrictCSP) {
294
// Read nonce from meta tag
295
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
296
if (nonceMeta?.content) {
297
style.nonce = nonceMeta.content;
298
}
299
}
300
301
style.appendChild(document.createTextNode(cssCode));
302
document.head.appendChild(style);
303
}
304
} catch (error) {
305
console.error('CSS injection failed:', error);
306
}
307
}
308
```
309
310
**Required HTML meta tag:**
311
```html
312
<meta property="csp-nonce" content="your-nonce-value" />
313
```
314
315
### Error Handling Best Practices
316
317
Custom injection functions should include proper error handling:
318
319
```typescript
320
injectCodeFunction: (cssCode, options) => {
321
try {
322
// Check for document availability (SSR compatibility)
323
if (typeof document === 'undefined') {
324
return;
325
}
326
327
// Create and configure style element
328
const style = document.createElement('style');
329
330
// Apply all options safely
331
if (options.styleId) {
332
style.id = options.styleId;
333
}
334
335
if (options.attributes) {
336
Object.entries(options.attributes).forEach(([key, value]) => {
337
try {
338
style.setAttribute(key, value);
339
} catch (attrError) {
340
console.warn(`Failed to set attribute ${key}:`, attrError);
341
}
342
});
343
}
344
345
if (options.useStrictCSP) {
346
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
347
if (nonceMeta?.content) {
348
style.nonce = nonceMeta.content;
349
}
350
}
351
352
// Insert CSS content
353
style.appendChild(document.createTextNode(cssCode));
354
document.head.appendChild(style);
355
356
} catch (error) {
357
// Use the plugin's error prefix for consistency
358
console.error('vite-plugin-css-injected-by-js:', error);
359
}
360
}
361
```