Search code examples
cssbootstrap-4web-componentshadow-domlit-element

Using bootstrap in web components shadowDOM


What's the easiest way to use a large css library (like bootstrap) in web components / shadowDOM app using LitElement?
Tried the following:

  1. Use the link tag within the component. Works, but creates FOUC (flash of unstyled content).
  2. Render everything to Light DOM (I'm using LitElement and they have a createRenderRoot() override. Works as well, but as the app gets more complex, maintaining component document isolation would be nice.

Looking for the simplest way to just use boostrap in this setting.


Solution

  • LitElement's recommended way to add styles to components is via the styles property. Loading external .css files this way is not straightforward, but there are some solutions.

    The import way

    If your definition of "simplest way" comprises using transpilers or module bundlers then you can use non-js inlined imports to accomplish something like the following:

    import bootstrap from './path/to/bootstrap.css';
    // ...
    
    class MyElement extends LitElement {
    
      static styles = bootstrap; // If your build system already converted
                                 // the stylesheet to a CSSResult
    
      static styles = unsafeCss(bootstrap); // If bootstrap is plain text
    
    }
    

    There are many plugins dedicated to this: see for example babel-plugin-inline-import, rollup-plugin-lit-css, rollup-plugin-postcss-lit, webpack-lit-loader.

    The wrapper way

    If you want to keep things (almost) buildless you can write a simple postinstall script that generates a .js file that exports the lit-ified styles:

    // bootstrap.css.js
    import {css} from 'lit-element';
    
    export const bootstrap = css`
    <bootstrap here>
    `;
    
    // my-element.js
    import {bootstrap} from './bootstrap.css.js';
    
    class MyElement extends LitElement {
      static styles = bootstrap;
    }
    

    About Shadow DOM

    If you want to use Shadow DOM you'll have to import the library in every component that needs to use it, even nested ones. This is not as onerous as it seems thanks to Constructable Stylesheets, used by Lit under the hood; think of it as a way for components to join style contexts more than a replication of identical stylesheets. Also, to keep things organized you can create a "base" component that imports bootstrap and extend it wherever needed:

    import bootstrap from 'path/to/bootstrap.css';
    
    export class BaseElement extends LitElement {
      static styles = bootstrap;
    }
    
    class MyElement extends BaseElement {
      render() {
        // Bootstrap is ready to use here!
        return html``;
      }
    }
    

    Lit documentation about style sharing: https://lit.dev/docs/components/styles/#sharing-styles