Skip to main content
One of the most critical features of Vue Print It is its ability to preserve your application’s styles when printing. This page explains the internal mechanisms that make this possible.

The Challenge

When you print content in a browser using window.print() or by creating a new window, the printed content loses its styling because:
  • The new print window is a separate document context
  • Stylesheets linked in the parent page aren’t automatically available
  • Computed styles from Vue components don’t transfer
  • CSS-in-JS and scoped styles require special handling

Style Preservation Architecture

Vue Print It solves this through a multi-layered approach:

Style Injection Methods

1. Inline Style Elements

The first step is copying all <style> tags from the page:
// From usePrint.ts:12-17
const styleElements = document.querySelectorAll('style');
styleElements.forEach(style => {
  const newStyle = win.document.createElement('style');
  newStyle.textContent = style.textContent;
  win.document.head.appendChild(newStyle);
});
This captures:
  • Component-scoped styles
  • Inline CSS blocks
  • Dynamically injected styles from CSS-in-JS libraries

2. External Stylesheets

Next, all linked stylesheets are copied:
// From usePrint.ts:19-26
const linkElements = document.querySelectorAll('link[rel="stylesheet"]');
linkElements.forEach(link => {
  const newLink = win.document.createElement('link');
  newLink.rel = 'stylesheet';
  newLink.href = (link as HTMLLinkElement).href;
  win.document.head.appendChild(newLink);
});
This includes:
  • External CSS files (e.g., Bootstrap, Tailwind)
  • CSS frameworks
  • Font stylesheets (Google Fonts, etc.)

3. CSS Rules Extraction

For more robust style preservation, the plugin also extracts CSS rules directly:
// From usePrint.ts:28-51
try {
  const cssRules: string[] = [];
  Array.from(document.styleSheets).forEach(sheet => {
    try {
      if (sheet.cssRules) {
        Array.from(sheet.cssRules).forEach(rule => {
          cssRules.push(rule.cssText);
        });
      }
    } catch {
      // Ignore CORS errors
    }
  });
  
  if (cssRules.length > 0) {
    const styleElement = win.document.createElement('style');
    styleElement.textContent = cssRules.join('\n');
    win.document.head.appendChild(styleElement);
  }
} catch {
  // Ignore errors
}
CORS Restrictions: Stylesheets loaded from different origins cannot be accessed due to browser security. These are handled gracefully with fallback to linked stylesheets.

Custom Style Injection

In addition to automatic style preservation, you can inject custom styles:
// From usePrint.ts:59-75
async function injectCustomStyles(win: Window, styles: string[]): Promise<void> {
  styles.forEach(style => {
    if (style.startsWith('http') || style.startsWith('/') || style.startsWith('./')) {
      // It's a stylesheet URL
      const link = win.document.createElement('link');
      link.rel = 'stylesheet';
      link.type = 'text/css';
      link.href = style;
      win.document.head.appendChild(link);
    } else {
      // It's inline CSS
      const styleElement = win.document.createElement('style');
      styleElement.textContent = style;
      win.document.head.appendChild(styleElement);
    }
  });
}
This function intelligently detects whether the style is:
  • A URL (creates a <link> element)
  • Raw CSS (creates a <style> element)

Usage Examples

Browser Print Window

When using the browser print method, styles are injected into the new window:
// From usePrint.ts:251-273
win.document.documentElement.innerHTML = `
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>${options.windowTitle}</title>
    </head>
    <body>
      ${targetElement.innerHTML}
    </body>
  </html>
`;

// Inject styles
if (options.preserveStyles) {
  await injectPageStyles(win);
}

const allStyles = [...(globalOptions.styles || []), ...(options.styles || [])];
if (allStyles.length > 0) {
  await injectCustomStyles(win, allStyles);
}

Bridge Print Method

For bridge printing, styles are embedded directly in the HTML string:
// From usePrint.ts:121-164
function buildPrintHtml(targetElement: HTMLElement, options: any, globalOptions: any): string {
  let htmlContent = `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>${options.windowTitle}</title>
  `;
  
  // Add styles if preserveStyles is enabled
  if (options.preserveStyles) {
    // Copy inline styles
    const styleElements = document.querySelectorAll('style');
    styleElements.forEach(style => {
      htmlContent += `<style>${style.textContent}</style>`;
    });
    
    // Copy external stylesheets
    const linkElements = document.querySelectorAll('link[rel="stylesheet"]');
    linkElements.forEach(link => {
      htmlContent += `<link rel="stylesheet" href="${(link as HTMLLinkElement).href}">`;
    });
  }
  
  // Add custom styles
  const allStyles = [...(globalOptions.styles || []), ...(options.styles || [])];
  allStyles.forEach(style => {
    if (style.startsWith('http') || style.startsWith('/') || style.startsWith('./')) {
      htmlContent += `<link rel="stylesheet" href="${style}">`;
    } else {
      htmlContent += `<style>${style}</style>`;
    }
  });
  
  htmlContent += `
      </head>
      <body>
        ${targetElement.innerHTML}
      </body>
    </html>
  `;
  
  return htmlContent;
}

Configuration Options

preserveStyles
boolean
default:"true"
Controls whether page styles are automatically preserved. Set to false to disable automatic style injection.
styles
string[]
default:"[]"
Array of custom CSS strings or stylesheet URLs to inject. Supports both inline CSS and external stylesheets.

Examples

app.use(createVuePrintIt({
  preserveStyles: true,
  styles: [
    'body { font-family: Arial, sans-serif; }',
    '@media print { .no-print { display: none; } }',
    'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700'
  ]
}))

Best Practices

Add @media print queries to your global styles or inject them per-call:
@media print {
  .no-print { display: none !important; }
  .page-break { page-break-after: always; }
  body { background: white; color: black; }
}
When using external stylesheets, ensure they’re accessible from the print context:
styles: [
  // Use absolute URLs for external stylesheets
  'https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css',
  
  // Or use relative paths from your app's base URL
  '/assets/print-styles.css'
]
Stylesheets from different domains may be blocked by CORS. Always provide fallback:
preserveStyles: true, // Attempts to copy linked styles
styles: [
  // Fallback: include critical styles inline
  'body { font-family: Arial; color: black; }'
]
For large applications, consider disabling automatic style preservation and manually specify required styles:
preserveStyles: false, // Don't copy everything
styles: [
  // Only include styles needed for printing
  '/css/print-only.css'
]

Common Issues and Solutions

Problem: Print output has no styling.Solutions:
  1. Verify preserveStyles: true (it’s the default)
  2. Check browser console for CORS errors
  3. Add styles explicitly via the styles option
  4. Ensure stylesheets are loaded before printing
Problem: Cannot access stylesheets from CDN.Solutions:
  1. The plugin automatically falls back to linking the stylesheet
  2. Provide inline fallback styles in the styles array
  3. Host critical stylesheets on the same domain
Problem: Component scoped styles don’t appear in print.Solutions:
  1. Ensure components are rendered before printing
  2. Scoped styles are automatically captured from <style> tags
  3. If using CSS-in-JS, ensure styles are injected into the DOM
Problem: Background colors and images don’t appear.Solutions: This is a browser default behavior. Override with:
@media print {
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
}

Advanced: Style Timing

The plugin uses the timeout option to ensure styles are fully loaded:
await new Promise<void>((resolve) => {
  setTimeout(async () => {
    win.focus();
    win.print();
    
    if (options.autoClose) {
      win.close();
    }
    
    resolve();
  }, options.timeout); // Default: 1000ms
});
For applications with many external stylesheets, increase the timeout:
await print('content', {
  timeout: 2000 // Wait 2 seconds for all stylesheets to load
})

Next Steps

Plugin System

Learn how the plugin architecture works

Bridge Printing

Explore direct printer communication

Build docs developers (and LLMs) love