Is CSS @import still considered as an anti-pattern?

Many years ago it was clear, that CSS “@import” was an antipattern because of many problems:

  • some browsers (e.g. old IE) included CSS @import styles not in the right order
  • import() requests were made not in parallel
  • with HTTP1, in many cases, it was easier for performance/development to avoid additional CSS requests, and simply concatenate the CSS files into one

Here are some links with descriptions:

But today we are switching to HTTP/2 and native ECMAScript modules “imports”, which have a bit similar behavior. All the modern browsers fixed the CSS @import problems, mentioned before, loading them in parallel and including styles in the right order.

The only concerns, I see so far, may be loading concerns, but it depends on the app architecture and will work kinda same as native ES6 modules. The additional requests problem will be solved by HTTP/2.

Of course, the difference with native ES6 modules is that you can use @import only at the beginning of the CSS file, but in the case of JS they are hoisting, but use “import” in JS only on the top level of the code- so there are still similar limitations.

What issues am I missing and what do you think, is CSS @import still considered as an anti-pattern?

I’ve messed around with this before, and as far as I can tell, they’re equivalent in modern browsers. @import is shorter for multiple stylesheets:

<link rel="stylesheet" href="a.css">
<link rel="stylesheet" href="b.css">
<link rel="stylesheet" href="c.css">
@import "a.css";
@import "b.css";
@import "c.css";

@import can take a media query, just like link[media], so that use-case is already accounted for. It may not work with alternate stylesheets, but that feature is pretty dead.

1 Like

Also even without H2 necessarily, <link rel="preload" href="/styles/other.css" as="style"> will do the trick loading up the resource before the import is hit to save the time. However, too many preload links and the UA may start ignoring them. There is a fine balance here.

I think this is only partially true. If you add an @import in another stylesheet, the browser first has to download this stylesheet and can only then download the imported stylesheet. This can only happen sequentially and this behavior is obviously unfixable.

So using @import inside of blocking head stylesheet is still a anti pattern. For an async loading stylesheet it might be not.

That’s true of any stylesheet in the <head> that isn’t async. No matter where they come from, stylesheets are considered critical content and block rendering since they all must be parsed before any rendering can continue.

The issue with @import before these preload/multiplexing features existed was the delayed time for the extra network requests to occur and then the time for them to happen. Now we have plenty of ways to work around those issues.

I wouldn’t say @import is necessarily an antipattern still, however it is something that should still be implemented with caution, care, and plenty of perf testing to make sure users do get the best experience.

If I put two stylesheets in my head they are loaded in parallel. But if I put one stylesheet in my head and this one references another one using @import they are loaded in sequence. This is the problem that I was talking about and this sequential loading is by no means true for all blocking stylesheets in the head.

Of course you can use preload/push techniques to workaround this problem. But this doesn’t justify to say: @import is not a problem anymore. The fact that you need to actively work around with other techniques, shows that there are still problems with this technique.

The question with using preload as a workaround is that if you’re going to use preload you’ve added another tag into your head, if you know at that point you need the other stylesheet why not just add the stylesheet there instead of @import?

Also unless you have the preloading injected during build you’re going to be manually adding/changing them which just gives another potential place to forget to update if you change your imports.