Styles

Qwik does not enforce a specific styling approach, you can style your Qwik app using any method you prefer, such as CSS, CSS-in-JS, CSS modules...

CSS Modules

Qwik supports CSS Modules out of the box thanks to Vite.

To use CSS modules, simply create a .module.css file. For example, MyComponent.module.css and import it in your component.

src/components/MyComponent/MyComponent.module.css
.container {
  background-color: red;
}

Then, import the CSS module in your component.

src/components/MyComponent/MyComponent.tsx
import { component$ } from '@builder.io/qwik';
import styles from './MyComponent.module.css';
 
export default component$(() => {
  return <div class={styles.container}>Hello world</div>;
});

Remember that Qwik uses class instead of className for CSS classes.

Qwik also accepts arrays, objects, or a combination of them to assign multiple classes:

src/components/MyComponent/MyComponent.tsx
import { component$ } from '@builder.io/qwik';
import styles from './MyComponent.module.css';
 
export default component$((props) => {
  // Array syntax example
  return <div class={[
    styles.container, 
    'p-8', 
    props.isHighAttention ? 'text-green-500' : 'text-slate-500',
    { active: true}
  ]}>Hello world</div>;
 
  // Object syntax example
  return <div class={{  
    'text-green-500': props.isHighAttention,
    'p-4': true
  }}>Hello world</div>;
});

Global Styles

Many apps use a global stylesheet for browser resets and 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 download the styles that are needed for the current view. If you use a global stylesheet, all of 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 an extremely efficient css-in-js solution without any runtime!

style.css.ts
import { style } from 'styled-vanilla-extract/qwik';
 
export const blueClass = style({
  display: 'block',
  width: '100%',
  height: '500px',
  background: 'blue',
});
component.tsx
import { component$ } from '@builder.io/qwik';
import { blueClass } from './styles.css';
 
export const Cmp = component$(() => {
  return <div class={blueClass} />;
});
pnpm 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 don't have a good SSR streaming support, leading to a degraded server and client performance.

Styled-components

The styled-components library is a popular tool in React-land for writing 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!

pnpm 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 { component$ } from '@builder.io/qwik';
import { BlueBox } from './styles.css';
 
export const Cmp = component$(() => {
  return <BlueBox />;
});

Scoped CSS

To use scoped CSS, you can use the useStylesScoped$() hook exported from @builder.io/qwik.

src/components/MyComponent/MyComponent.tsx
import { component$, useStylesScoped$ } from '@builder.io/qwik';
 
export default component$(() => {
  useStylesScoped$(`
    .container {
      background-color: red;
    }
  `);
  return <div class="container">Hello world</div>;
});

You can also import an external CSS file. For that you need to add the ?inline query parameter to the import of the CSS file, and pass the default export of the CSS file to the useStyleScoped$() hook.

src/components/MyComponent/MyComponent.css
.container {
  background-color: red;
}
src/components/MyComponent/MyComponent.tsx
import { component$, useStylesScoped$ } from '@builder.io/qwik';
 
import styles from './MyComponent.css?inline';
 
export default component$(() => {
  useStylesScoped$(styles);
  return <div class="container">Hello world</div>;
});

: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$, component$ } 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.

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, which avoids double loading during SSR hydration.

import { useStyles$, component$ } 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';

useStyles$ uses lazy loading strings that won't dynamically update. If you'd like to evaluate some JS, use a style tag instead

export const CmpStyles = component$(() => {
  const primaryColor = "red";
 
  // โŒ Does not work
  useStyles$(`
    .my-text {
      --primary-color: ${primaryColor};
    }
  `);
 
  // โœ… Adding in the style attribute works
  return (
    <span 
      style={{ "--primary-color": primaryColor }} 
      class="my-text"
    >
      Some text
    </span>
  );
});

CSS Preprocessors

Thanks to Vite, Qwik supports CSS preprocessors like Sass, Less, Stylus, and PostCSS.

There is no need to install Qwik-specific plugins for them, but the corresponding pre-processor itself must be installed:

# .scss and .sass
pnpm add -D sass
 
# .less
pnpm add -D less
 
# .styl and .stylus
pnpm add -D stylus

Check Vite's docs for more information.

Tailwind

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

pnpm run qwik add tailwind

Check out the integration docs for more information.

PostCSS

It is also possible to use PostCSS in your app with our built-in integration:

pnpm run qwik add postcss

Important: Since we are using vite, the configuration should look as follows to work:

// Configuration with vite
module.exports = {
  plugins: {
    autoprefixer: {},
    "postcss-preset-env": {
      stage: 3,
      features: {
        "nesting-rules": true,
      },
    },
  },
}

Now you will be able to use CSS with nesting-rules like the following ones:

body {
  & .box {
    background: red;
 
    &:hover {
      background: yellow;
    }
  }
}

Check out 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>.

Contributors

Thanks to all the contributors who have helped make this documentation better!

  • manucorporat
  • zanettin
  • cunzaizhuyi
  • manuelsanchez2
  • literalpie
  • forresst
  • DustinJSilk
  • saikatdas0790
  • LiKang6688
  • Craiqser
  • adamdbradley
  • the-r3aper7
  • mhevery
  • igorbabko
  • mrhoodz
  • tanftw