A Vite plugin that injects CSS into JavaScript bundles for single-file applications.
—
Advanced customization of CSS injection behavior with custom JavaScript code and runtime functions.
Type definitions for custom CSS injection functions.
/**
* Function that returns JavaScript code string for CSS injection
* @param cssCode - CSS code to inject (as string literal)
* @param options - Injection configuration options
* @returns JavaScript code string that will inject the CSS
*/
type InjectCode = (cssCode: string, options: InjectCodeOptions) => string;
/**
* Runtime function that directly injects CSS into the DOM
* @param cssCode - CSS code to inject
* @param options - Injection configuration options
*/
type InjectCodeFunction = (cssCode: string, options: InjectCodeOptions) => void;
/**
* Options passed to injection functions
*/
interface InjectCodeOptions {
/** Style element ID or generator function */
styleId?: string | (() => string);
/** Enable strict CSP support with nonce */
useStrictCSP?: boolean;
/** Additional attributes for the style element */
attributes?: { [key: string]: string } | undefined;
}Provide a function that returns JavaScript code for CSS injection.
/**
* Custom CSS injection code function
* Returns JavaScript code that will be executed to inject CSS
*/
injectCode?: InjectCode;Usage Examples:
import { defineConfig } from "vite";
import cssInjectedByJsPlugin from "vite-plugin-css-injected-by-js";
// Basic custom injection code
export default defineConfig({
plugins: [
cssInjectedByJsPlugin({
injectCode: (cssCode, options) => {
return `
try {
if (typeof document !== 'undefined') {
var style = document.createElement('style');
${options.styleId ? `style.id = '${options.styleId}';` : ''}
style.appendChild(document.createTextNode(${cssCode}));
document.head.appendChild(style);
}
} catch (e) {
console.error('CSS injection failed:', e);
}
`;
},
}),
],
});
// Advanced injection with error handling and CSP
export default defineConfig({
plugins: [
cssInjectedByJsPlugin({
injectCode: (cssCode, options) => {
let attributeCode = '';
// Add style ID if provided
if (options.styleId) {
attributeCode += `style.id = '${options.styleId}';`;
}
// Add CSP nonce if enabled
if (options.useStrictCSP) {
attributeCode += `
var nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
if (nonceMeta) style.nonce = nonceMeta.content;
`;
}
// Add custom attributes
if (options.attributes) {
for (const [key, value] of Object.entries(options.attributes)) {
attributeCode += `style.setAttribute('${key}', '${value}');`;
}
}
return `
try {
if (typeof document !== 'undefined') {
var style = document.createElement('style');
${attributeCode}
style.appendChild(document.createTextNode(${cssCode}));
document.head.appendChild(style);
}
} catch (e) {
console.error('vite-plugin-css-injected-by-js injection failed:', e);
}
`;
},
}),
],
});Provide a runtime function that directly injects CSS into the DOM.
/**
* Custom CSS injection runtime function
* Executed directly at runtime to inject CSS
*/
injectCodeFunction?: InjectCodeFunction;Usage Examples:
// Basic runtime injection function
export default defineConfig({
plugins: [
cssInjectedByJsPlugin({
injectCodeFunction: (cssCode, options) => {
try {
if (typeof document !== 'undefined') {
const style = document.createElement('style');
// Set style ID if provided
if (options.styleId) {
style.id = options.styleId;
}
// Set CSP nonce if enabled
if (options.useStrictCSP) {
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
if (nonceMeta) {
style.nonce = nonceMeta.content;
}
}
// Set custom attributes
if (options.attributes) {
for (const [key, value] of Object.entries(options.attributes)) {
style.setAttribute(key, value);
}
}
style.appendChild(document.createTextNode(cssCode));
document.head.appendChild(style);
}
} catch (error) {
console.error('CSS injection failed:', error);
}
},
}),
],
});
// Advanced runtime function with insertion control
export default defineConfig({
plugins: [
cssInjectedByJsPlugin({
injectCodeFunction: (cssCode, options) => {
try {
if (typeof document !== 'undefined') {
const style = document.createElement('style');
// Configure style element
if (options.styleId) {
style.id = options.styleId;
}
// Add all custom attributes
if (options.attributes) {
Object.entries(options.attributes).forEach(([key, value]) => {
style.setAttribute(key, value);
});
}
// Handle CSP nonce
if (options.useStrictCSP) {
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
if (nonceMeta?.content) {
style.nonce = nonceMeta.content;
}
}
// Insert CSS content
style.appendChild(document.createTextNode(cssCode));
// Insert at specific position (after title if exists)
const title = document.head.querySelector('title');
if (title) {
document.head.insertBefore(style, title.nextSibling);
} else {
document.head.appendChild(style);
}
}
} catch (error) {
console.error('vite-plugin-css-injected-by-js:', error);
}
},
}),
],
});Configuration options passed to custom injection functions.
/**
* Options passed to injection functions
*/
interface InjectCodeOptions {
/** Style element ID or generator function */
styleId?: string | (() => string);
/** Enable strict CSP support with nonce */
useStrictCSP?: boolean;
/** Additional attributes for the style element */
attributes?: { [key: string]: string } | undefined;
}Option Details:
styleId: Sets the id attribute on the created style elementuseStrictCSP: Enables Content Security Policy support by reading nonce from meta tagattributes: Additional attributes to set on the style element (includes development mode attributes)Custom injection functions work with development mode and receive additional attributes:
// Development mode aware injection
injectCodeFunction: (cssCode, options) => {
try {
if (typeof document !== 'undefined') {
const style = document.createElement('style');
// Handle all attributes (including dev mode attributes)
if (options.attributes) {
Object.entries(options.attributes).forEach(([key, value]) => {
style.setAttribute(key, value);
});
}
// In development mode, attributes will include:
// { "data-vite-dev-id": "path/to/file.css" }
style.appendChild(document.createTextNode(cssCode));
document.head.appendChild(style);
}
} catch (error) {
console.error('CSS injection failed:', error);
}
}When useStrictCSP is enabled, injection functions should read the nonce from a meta tag:
// CSP-compliant injection
injectCodeFunction: (cssCode, options) => {
try {
if (typeof document !== 'undefined') {
const style = document.createElement('style');
if (options.useStrictCSP) {
// Read nonce from meta tag
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
if (nonceMeta?.content) {
style.nonce = nonceMeta.content;
}
}
style.appendChild(document.createTextNode(cssCode));
document.head.appendChild(style);
}
} catch (error) {
console.error('CSS injection failed:', error);
}
}Required HTML meta tag:
<meta property="csp-nonce" content="your-nonce-value" />Custom injection functions should include proper error handling:
injectCodeFunction: (cssCode, options) => {
try {
// Check for document availability (SSR compatibility)
if (typeof document === 'undefined') {
return;
}
// Create and configure style element
const style = document.createElement('style');
// Apply all options safely
if (options.styleId) {
style.id = options.styleId;
}
if (options.attributes) {
Object.entries(options.attributes).forEach(([key, value]) => {
try {
style.setAttribute(key, value);
} catch (attrError) {
console.warn(`Failed to set attribute ${key}:`, attrError);
}
});
}
if (options.useStrictCSP) {
const nonceMeta = document.head.querySelector('meta[property="csp-nonce"]');
if (nonceMeta?.content) {
style.nonce = nonceMeta.content;
}
}
// Insert CSS content
style.appendChild(document.createTextNode(cssCode));
document.head.appendChild(style);
} catch (error) {
// Use the plugin's error prefix for consistency
console.error('vite-plugin-css-injected-by-js:', error);
}
}Install with Tessl CLI
npx tessl i tessl/npm-vite-plugin-css-injected-by-js