Styles

Global styles

Many apps use a global stylesheet to do browser resets and/or defining global styles. This is a good practice, but it is not recommended to use it for styling your components. Qwik is optimized to let the browser just download the styles that are needed for the current view. If you use a global stylesheet, all the styles will be downloaded on the first load, even if they are not needed for the current view.

import './global.css';

Automatically, Qwik will try to inline this file in production mode if the amount of CSS is less than 10KB. If the file is larger than 10KB, it will be loaded as a separate file.

CSS-in-JS

Qwik has first-class CSS-in-JS support using styled-vanilla-extract, which provides a extremely efficient css-in-js solution without any runtime!

styles.css.ts:

import { style } from 'styled-vanilla-extract/qwik';

export const blueClass = style({
  display: 'block',
  width: '100%',
  height: '500px',
  background: 'blue',
});

component.tsx:

import { blueClass } from './styles.css';

export const Cmp = component$(() => {
  return <div class={blueClass} />;
});
npm run qwik add styled-vanilla-extract

Please refer to the docs of our official integration for more information.

How about emotion or other CSS-in-JS libs? While extremely popular, emotion and other CSS-in-JS libs are not the best choice for Qwik. They are not optimized for runtime performance and they dont have a good SSR streaming support, leading to a degraded server and client performance.

Styled-components

Styled components is a popular tool in React-land to write CSS-in-JS. Thanks to the same styled-vanilla-extract plugin, you can write your styles with styled-components syntax in Qwik with zero-runtime cost!

npm run qwik add styled-vanilla-extract

Like this:

styles.css.ts:

import { styled } from 'styled-vanilla-extract/qwik';

export const BlueBox = styled.div`
  display: block;
  width: 100%;
  height: 500px;
  background: blue;
`;

component.tsx:

import { BlueBox } from './styles.css';

export const Cmp = component$(() => {
  return <BlueBox />;
});

Per-component styles

Qwik provided built-in support for per-component styles, this technology allows Qwik to only inject the provided styles only if the component is used in the current page, this can be achieved by using the useStyles$() and useStylesScoped$() hook.

useStyles$()

A lazy-loadable reference to component's styles.

Component styles allow Qwik to lazy load the style information for the component only when needed. (And avoid double loading it in case of SSR hydration.)

import { useStyles$ } from '@builder.io/qwik';
import styles from './code-block.css?inline';

export const CmpStyles = component$(() => {
  useStyles$(styles);
  return <span class="my-text">Some text</span>;
});
// code-block.css
.my-text {
  color: red;
}

Notice that in order to import CSS as a string in Vite, you need to add the ?inline query parameter to the import, like this: import styles from './code-block.css?inline';

useStylesScoped$

In previous sections, we have discussed how styles can be loaded lazily as they are needed using the useStyles$() hook. Browser styles are global and apply to all DOM elements, for this reason, Qwik also provides a way to load styles that are specific to a specific component. This is achieved by generating a unique class for each component and then inserting that unique class id into the stylesheet.

import { useStylesScoped$ } from '@builder.io/qwik';
import styles from './code-block.css?inline';

export const CmpStyles = component$(() => {
  useStylesScoped$(styles);
  return <span class="my-text">Some text</span>;
});
// code-block.css
.my-text {
  color: blue;
}

Please note that you need to add ?inline to your styles import.

:global() selector

Using useStylesScoped$ will scope all child selectors in a ruleset to the component. If you need to style child components rendered through a <Slot />, you will need to break out of the scoped styles using the :global() selector.

import { useStylesScoped$ } from '@builder.io/qwik';

export const List = component$(() => {
  useStylesScoped$(`
    .list {
      display: flex;

      > :global(*nth-child(3)) {
        width: 100%
      }
    }
  `);

  return (
    <div class="list">
      <Slot />
    </div>;
  );
});

This will render a css selector of .list.⭐️8vzca0-0 > *:nth-child(3), allowing you to target child components. This could be considered the equivalent of using ::ng-deep in Angular.

Beware that this may have unintended effects that cascade down your component tree.

Preprocessors

.scss, .sass, .less, .styl, .stylus

Vite works nicely with many preprocessors. Just make sure, that you install the required plugins.

Tailwind

To use Tailwind on your app, you can add it to you app with our built-in integration:

npm run qwik add tailwind

Check our the integration docs for more information.

Why not inline styles using the <style> tag?

A naive way to ensure that a component has the correct styles loaded is to inline the style information into a component like so.

export const MyComponent = () => {
  return (
    <>
      <style>.my-class { color: red; }</style>
      My Component
    </>
  );
}

The problem with this approach is that we will load styles twice.

  1. The styles are inserted into the HTML as part of the SSR.
  2. Then when the component is invalidated and needs to be re-rendered, the styles are loaded again because they are inlined.

What is needed is to load the styles independently from the component. This is what useStyles$() is for. There are two scenarios:

  1. The component is rendered on the server and the styles are inserted into <head> as part of the SSR.
    • Adding a new instance of a component to the application does not require that we load the styles as they are already included as part of SSR.
  2. The component is rendered on the client for the first time. In that case, the new component does not have styles in the <head> as the component was not part of SSR.
    • Adding a new component that was not part of SSR requires that styles are loaded and inserted into <head>.
Made with ❤️ by