next.js
next.js copied to clipboard
Compiler does not place css @imports in own style tags the way babel used to.
What version of Next.js are you using?
12.0.7
What version of Node.js are you using?
14.18.1
What browser are you using?
chrome
What operating system are you using?
mac0s
How are you deploying your application?
vercel
Describe the Bug
I just got bit in the upgrade.
In nextjs v10, this is considered valid:
* {
font-family: "Abril Fatface";
}
@import url('https://fonts.googleapis.com/css2?family=Abril+Fatface&display=block');
This works because each @import
statement is wrapped in its own style
tag.
In nextjs v12, @import
statements are not wrapped in their own style
tag.
Expected Behavior
I expect each @import
statement is wrapped in its own style
tag.
To Reproduce
In nextjs v12, add this to global.css
* {
font-family: "Abril Fatface";
}
@import url('https://fonts.googleapis.com/css2?family=Abril+Fatface&display=block');
The move to SWC does not affect .css
files, can you provide a full reproduction?
I've made a repo at https://github.com/scottccoates/nextjs-12-css-test.
To reproduce:
-
cd next-10
-
npm run dev
- Observe the sparkly font
- Stop the process
-
cd ../next-12
-
npm run dev
- The sparkly font is now gone
Observe the global.css
in both folders. They are identical. But it does not work on Next v12. This is because in prior versions, @import
statements (found in global.css
) are "hoisted" into their own script tags.
I can confirm this as an issue, but as Tim said, it's not related to SWC. (I tried adding a .babelrc
file, and this still reproduces)
I could narrow it down to this change: https://github.com/vercel/next.js/compare/v11.1.3-canary.99...v11.1.3-canary.100
Most likely this PR #30165
Prior this, the generated CSS looked like this:
@import url(https://fonts.googleapis.com/css2?family=Emilys+Candy&display=block);
body,html {
padding: 0;
margin: 0;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif
}
a {
color: inherit;
text-decoration: none
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-family: Emilys Candy
}
But after, it was left untouched:
body,html {
padding: 0;
margin: 0;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif
}
a {
color: inherit;
text-decoration: none
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-family: Emilys Candy
}
@import url("https://fonts.googleapis.com/css2?family=Emilys+Candy&display=block");
#30567 and #30569 might be related
Interesting find, @balazsorban44, about the changed behavior with @import
being hoisted to the top of the compiled file. Using the repro repo I was able to confirm that the font importing still works if:
- The import is placed in the CSS before the font is referenced.
- The SASS compiler is used to compile global as an
scss
file instead of acss
one (sass apparently does the import-to-top hoisting itself).
Per #30567:
Loading the font with next dev works but not with next build (next start).
I can confirm that importing fonts works in dev but not with next build. Generated styles in production are not valid.
Interesting @scottccoates - it appeared to work for me with a production build in the cases I listed above. Do you mean it doesn't work in only the case of having @import
below the font's usage in a plain .css file? Or does it fail for you in scss files and/or even if the import is above the font's usage?
@zackdotcomputer We somehow hit the daily limit for deploys on vercel. I will have to wait but I'll get back to you asap.
@zackdotcomputer I just realized I can run next build
to view what the bundled css files will look like in production (it will be named something like .next/static/css/5a1c6eae982d3adb11d2.css
).
After installing sass
and changing my global.css
to global.scss
, it is still not working the way I expect.
Do you mean it doesn't work in only the case of having @import below the font's usage in a plain .css file? Or does it fail for you in scss files and/or even if the import is above the font's usage?
It fails regardless of where the @import
statement occurs. I have this at the top of my global.scss
file:
@import url('https://fonts.googleapis.com/css2?family=Work+Sans&display=block');
@import url('https://fonts.googleapis.com/css2?family=Yeseva%20One&display=block');
Yet after running next build
, my bundled css file does not hoist these imports to the top.
If I go back to next 10 and run next build
, I can see these fonts are hoisted to the top of my bundled css file.
See the screenshots for more detail.
Next 10
Next 12
Update on previous comment:
I was importing bootstrap first in my _app.js
file, that was causing my issue:
import 'bootstrap/dist/css/bootstrap.min.css';
import '../styles/global.scss';
If i switch the import order, making the global css file first, it works, even without scss.
Confirming that @scottccoates mentioned above.
I had Tailwind definitions at the top of the file and then a font Google Font import afterwards like this
/* ./styles/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap");
Moving the imports as the top first thing on the file, solved the problem
/* ./styles/globals.css */
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
I also confirm this working correctly on Next 11
Hope this is useful.
Nah, moving @import ...
around doesn't fix anything for me.
My CSS files used to have @import
s at the top as seen here: https://guildplanner.pro/_next/static/css/4b3115bd6e9f5f53cd0c.css
But with NextJs 12, those leading @import
s are gone now.
Same problem with @import
on the top
I can confirm that this issue is still occurring with nextjs 12.1.0 production mode. Does anyone know any alternative way to load google fonts while using swcMinify?
Edit: The issue does not seem to be related to swc, and occurs even with swcMinify = false in nextjs 12+ versions; I worked around it in the following way:
- remove @import google fonts from the scss files
- new imports.css file that contains all the @import statements, which is imported first in _app.tsx This works with swc enabled too, production & dev versions.
For font loading, we recommend leveraging our built-in font optimization: https://nextjs.org/docs/basic-features/font-optimization
Thanks @balazsorban44 - while it would still be great to get this working with CSS/SCSS inline imports, it's good to know that link tags in head not only should work as an alternative but also give some optimization.
Inside of your purely JS/TS Next app, the guidance is to use @import
in a separate stylesheet and then place a CSS import into the /pages/_app.js
file:
https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet
I'm sure there is a technical reason for all of this but doing a CSS import into a JS app feels kind of bizarre to me. But it does fix this issue.
Really looks like the order of imports makes sense. Font @import
started working after I have rearranged my scss imports.
My font imports weren't working (using Emotion's Global component) and it was because I had them with comments before each one, removing comments and leaving only the imports at the start of the css solved this issue.
This works:
const globalCSS = `
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@300;400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap');
(other global styles...)
`;
and then:
<Global styles={css`${globalCSS}`} />