Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
The @import
at-rule merges a CSS file into another one. It’s written at the top of the document, before any other CSS (except @charset
and @layer
rules), so if we try to write any other rule before @import
, it will be invalid. Also, it’s worth noting upfront that imported CSS styles are interpreted as if they are inserted where @import
is written, so they are inserted at essentially the top of the stylesheet before any of your other styles.
And, finally, we can @import
CSS conditionally, based on users’ media features or if their browser supports a specific feature. We’ll look at all of this as we go along.
/* Imports other.css if the screen size is bigger than 600px` */
@import url("other.css") screen and (width > 600px);
There is one catch with @import
, as Geoff noted in his post on The At-Rules of CSS:
“With the popularity of CSS preprocessors that support
@import
, it should be noted that they work differently than native CSS.@import
in CSS is a separate HTTP request for that file.” So, that’s just a quick note to make sure you’re not confusing@import
in preprocessors with@import
in CSS.
Syntax
@import [ <url> | <string> ]
[ layer | layer(<layer-name>) ]?
<import-conditions> ;
<import-conditions> = [ supports( [ <supports-condition> | <declaration> ] ) ]?
<media-query-list>?
Arguments
/* Basic imports */
@import "other.css";
@import url("other.css");
/* Layered imports */
@import url("other.css") layer; /* anonymous layer */
@import url("other.css") layer(my-layer); /* named layer */
/* Media-dependant imports */
@import url("other.css") screen and (width > 600px);
@import url("other.css") screen and (prefers-color-scheme: dark);
/* Support-dependant imports */
@import url("other.css") supports(anchor-name: --anchor);
@import url("other.css") supports((display: grid) and (grid-template-columns: subgrid));
/* All combined */
@import url("narrow.css") layer(my-layer) supports(display: grid) screen and (width > 600px);
<url> | <string>
: The location of the file we’ll import. We can write just a string or enclose it in anurl()
function. It can also be a local or remote path.layer | layer(<layer-name>)
: Specifies a cascade layer that the CSS file is imported into, which can either be a new anonymous layer or a named layer.supports(<import-conditions>)
: Enclosed in asupports()
wrapper, lets us insert the stylesheet based on the browser’s support for a specific feature. It uses the same syntax as@supports
.<media-query-list>
: Inserts a stylesheet conditionally based on media features such as the screen width or the type of screen being used. It uses the same syntax as@media
.
Basic usage
Splitting and organizing smaller CSS files is usually better than one big stylesheet, and while it may not be the best appraoch, @import
can be used to divide our CSS into more manageable files. We can have some CSS written in a different file:
/* other.css */
body {
background-color: red;
}
And bring it onto a main CSS file using @import
followed by the location of the file:
/* style.css */
@import url("other.css");
The previous example will load other.css
into the main stylesheet, inserting its contents at the top of the document as if we had written them directly there.
Layered imports
Using CSS Cascade Layers, we can import a stylesheet directly into an anonymous or named layer. In the case of an anonymous layer, it’s enough to write the layer
keyword after our initial keyword:
@import url("other.css") layer; /* anonymous layer */
Since we can’t reference them, anonymous layers are evaluated in the order they are declared, so from top to bottom, where the bottom layers are more specific than those declared at the top.
On the other hand, we can import a stylesheet into a named layer by wrapping the layer’s name in the layer() wrapper function. For example, the following will load the other.css
file into the my-layer
layer.
@layer my-layer;
@import url("other.css") layer(my-layer); /* named layer */
Layers are a new world in and out of themselves, so you can learn more about them in our @layer
entry. You can also learn more in our CSS Cascade Layers Guide.
Conditional imports
We are supposed to write @import
rules at the beginning of the stylesheet, before any other rule. So importing a stylesheet walled behind a @media
or @supports
rule isn’t allowed, but that’s surely something we should be able to do, right?
Think about only importing a stylesheet when the user supports a certain feature or including a bolder font depending on the user’s prefer-color-scheme
media feature. In the @import
world, we can’t write such rules as we normally would:
/* DON'T DO THIS!! */
@media screen and (width > 600px) {
@import url("other.css");
}
/* DON'T DO THIS!! */
@supports (display: grid) {
@import url("other.css");
}
Instead, @import
has a built-in syntax to conditionally import CSS, depending on the browser support and the user’s media features. To only bring a stylesheet when the user supports a certain feature, we can write the property to check inside a supports()
wrapper function:
@import url("other.css") supports(anchor-name: --anchor);
The next demo should only import another CSS file if your browser supports anchor positioning:
We can also import things conditionally based on the user’s media feature, such as only importing a CSS file if the screen is bigger than 600px
:
@import url("other.css") screen and (width > 600px);
Importing CSS to JavaScript
There is a new (but also experimental) way to import CSS into your site using JavaScript. It uses the same syntax as ESM imports with the addition of the with
keyword followed by { type: "css" }
. This will tell JavaScript to parse that file as a CSSStyleSheet
object. Once loaded, we can use it on our site with the document.adoptedStyleSheets()
method.
import styles from "./other.css" with { type: "css" };
document.adoptedStyleSheets.push(styles);
Funny enough, @import
rules present in the stylesheet will be ignored. There is an open issue that discusses supporting them.
Why would we import CSS to JavaScript in the first place? The Chrome team lists three advantages:
- Deduplication: If the same CSS file is imported from multiple places in an application, it will still only be fetched, instantiated, and parsed once instead of happening in multiple requests.
- Consistent order of evaluation: When JavaScript is running the import it can rely on the stylesheet it imports having already been fetched and parsed.
- Security: Modules are fetched with CORS and use strict MIME-type checking.
If your browser supports importing CSS to JavaScript, then the following demo should return ta green background just like the rest of the demos on this page:
That same Chrome tutorial used the outdated assert {type: "css"}
instead of the standard with {type: "css"}
. So, while it’s a conceptual introduction to importing CSS in JavaScript, it still has some outdated snippets.
@import
?
When should you use In a nutshell, @import
lets you split your CSS into several files, so that should equate to… code-splitting, cleaner file structure, things can be loaded modularly, world peace, and a big et cetera. In practice, though, you’ll see a lot of hate on @import
, reading post titles like “Avoid CSS @import
” or “What Is CSS @import
And Why Can It Slow Down Websites?” While performance-metrics companies tend to be the ones writing those posts, there is truth @import
isn’t the most performant solution, and that it is usually frowned upon because of it.
But let’s backtrack a little. The main reason to split CSS into several files is Developer Experience (DX); working with smaller and more organized files is easier than one monster stylesheet. The problem comes when we load all those files into the page. Then there are two vanilla options, loading them using the everyday <link>
tag or using @import
.
And those two options lead to similar results but with a crucial difference: how the browsers load each file. In the case of using <link>
, the browser will try to load all files in parallel, while @import
will load each file one by one, hurting performance with longer page loads.
However, this is when we are talking about HTTP/1. We have lived in the world of HTTP/2 for over a decade. HTTP/2 is fully multiplexed, which means that multiple files and requests can be done at the same time. So, in theory, we can use @import
without worrying about performance. I must admit, though, that I have yet to find a benchmark to support this theory.
Specification
You can find the latest specification for @import
in the CSS Cascading and Inheritance Level 5 module.
Browser support
More information
- What Is CSS @import And Why Can It Slow Down Websites? (DebugBear)
- Using CSS Module Scripts to import stylesheets (Dan Clark) (Great conceptual intro; snippets are a little outdated)
- Importaciones: regla
@import
(Manz Dev) (Really good resource for Spanish Speakers)