Open jayu opened 4 years ago
@jayu any ideas when this might be updated?
@lifeiscontent Not a maintainer, but here are the instructions for modern Next.js: https://github.com/callstack/linaria/issues/447
How to use linaria css
api (not styled
) with nextjs?
It tries to inject files but got nextjs error
@chrisands can you spin up a repo with reproduction and open a new issue for that?
@jayu I'll try to do this week
Also seeing this issue -- set up linaria@2.0.0-alpha.1 and added:
const shaker = require('linaria/lib/babel/evaluators/shaker').default;
module.exports = {
displayName: process.env.NODE_ENV !== 'production',
rules: [
{
action: shaker,
},
{
test: /\/node_modules\//,
action: 'ignore',
},
],
};
I recently discovered Linaria and trying to make it work with Next.js.
One of the problems of current official example is that it uses @zeit/next-css
, which doesn't support CSS splitting, this is a huge disadvantage for me, so I tried to make built-in CSS support (available since 9.2) to play nice with linaria/loader
and ended up with this simple plugin: https://github.com/Mistereo/next-linaria.
Plugin changes extension to .linaria.module.css
so that Next.js loader picks Linaria files without complains and also overrides getLocalIdent to return unmodified localName
for Linaria classes.
Still testing it, but so far seems promising. Any possible issues with this approach?
@Mistereo I tried your plugin. It unfortunately seems that the Linaria's :global()
selector doesn't work with it, receiving errors like:
Error: Syntax error: Selector "html" is not pure (pure selectors must contain at least one local class or id)
Without your plugin, I need to use @zeit/next-css
, which means that I'm getting errors from mini-css-extract-plugin
eg.
chunk styles [mini-css-extract-plugin]
Conflicting order between:
Is there any new insight on how to configure linaria@3.0.0-beta.0
with next@10.0.1
in a way that doesn't have us jump through hoops? I don't mind some complexity in my configuration if that means we can have a somewhat simpler developer experience.
@Mistereo I tried your plugin. It unfortunately seems that the Linaria's
:global()
selector doesn't work with it, receiving errors like:Error: Syntax error: Selector "html" is not pure (pure selectors must contain at least one local class or id)
Thanks for sharing @nickrttn, I am too baffled by the same issue, was curious how your setup Next web app to use Linaria global CSS?
My current fallback workaround was simply using the build-in css support by Next.js with a standalone globals.css
file and import the it to _app.js
with import '../styles/globals.css';
. Does anyone know the implication of such setup? i.e. does that mean a CSS runtime also gets bundled at the end of the day (defeating the purpose of using Linaria in the first place) since this is using the built in CSS support by Next.js?
Tried adding the following to _app.js
didn't work either:
export const globals = css`
:global() {
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
}
`;
and got an error like:
error - .linaria-cache/pages/_app.linaria.module.css:4:13
Syntax error: Selector "html" is not pure (pure selectors must contain at least one local class or id)
2 | import { css } from '@linaria/core';
3 |
> 4 | export const globals = css`
| ^
5 | :global() {
6 | html {
Not 100% sure if it's just the way I had linaria configured, currently using next-linaria
by @Mistereo with .babelrc
as:
{
"presets": ["next/babel", "linaria/babel"]
}
Any guidance would be highly appreciated! Thanks for everyone's experience sharing thus far.
I'm having the same issue as @nickrttn and @joyfulelement . I've attempted to modify the babel configuration used within the next-linaria
plugin, but haven't made any progress yet... Our application is fairly complex, and we're migrating to Next.js from Gatsby, meaning there are a lot of moving pieces in the way of this working. I might be best to sandbox it and fix it in isolation. I'll check in again when I find something - this is critical for my team so we've got 2 people trying to work it out.
Thanks for sharing @nickrttn, I am too baffled by the same issue, was curious how your setup Next web app to use Linaria global CSS?
I've (sadly!) since moved to use Emotion in our application. We were trying out Linaria in a smaller project with a short timeline and there wasn't a lot of time to set up tooling unfortunately. I'll make sure to revisit Linaria as version 3 comes out of beta and sees some more adoption because the ideas make a lot of sense to me.
My current fallback workaround was simply using the build-in css support by Next.js with a standalone
globals.css
file and import the it to_app.js
withimport '../styles/globals.css';
. Does anyone know the implication of such setup? i.e. does that mean a CSS runtime also gets bundled at the end of the day (defeating the purpose of using Linaria in the first place) since this is using the built in CSS support by Next.js?
I think that's a good solution for now. Importing a .css
file should also output a CSS file on the webpack end and afaik wouldn't use any runtime like the styled-jsx
one that Next.js includes.
FWIW, with-linaria
uses next-linaria
since https://github.com/vercel/next.js/pull/23332
Of course :global()
still doesn't work. Anyone have ideas for solving that?
Here is the fix, that I believe will be in next release if next-linaria
- https://github.com/callstack/linaria/issues/724#issuecomment-853063166
@M1r1k I'm not convinced there's going to be a next release of next-linaria
. The with-linaria
example should probably just use the raw code, or someone should fork it. Here's the updates needed for global support
Anyone have a working example with NextJS 12 with Typescript and linaria 3 beta?
Linaria 4 no longer works with Nextjs 12. At least not the dynamic properties. Version 2 works normally.
I got this working with NextJS 12 & Linaria 4. Just walking back through my changes; will publish something shortly
@anulman can you push up an example for us?
Oop, sorry — only got it stable end-of-day Friday and cycled off the project this week.
// babel.config.js
module.exports = {
presets: ['next/babel', '@linaria'],
};
// next.config.js
const _ = require('lodash');
const BABEL_LOADER_STRING = 'babel/loader';
const makeLinariaLoaderConfig = (babelOptions) => ({
loader: require.resolve('@linaria/webpack-loader'),
options: {
sourceMap: true,
extension: '.linaria.module.css',
babelOptions: _.omit(
babelOptions,
'isServer',
'distDir',
'pagesDir',
'development',
'hasReactRefresh',
'hasJsxRuntime',
),
},
});
let injectedBabelLoader = false;
function crossRules(rules) {
for (const rule of rules) {
if (typeof rule.loader === 'string' && rule.loader.includes('css-loader')) {
if (
rule.options &&
rule.options.modules &&
typeof rule.options.modules.getLocalIdent === 'function'
) {
const nextGetLocalIdent = rule.options.modules.getLocalIdent;
rule.options.modules.mode = 'local';
rule.options.modules.auto = true;
rule.options.modules.exportGlobals = true;
rule.options.modules.exportOnlyLocals = true;
rule.options.modules.getLocalIdent = (context, _, exportName, options) => {
if (context.resourcePath.includes('.linaria.module.css')) {
return exportName;
}
return nextGetLocalIdent(context, _, exportName, options);
};
}
}
if (typeof rule.use === 'object') {
if (Array.isArray(rule.use)) {
const babelLoaderIndex = rule.use.findIndex(
({ loader }) => typeof loader === 'string' && loader.includes(BABEL_LOADER_STRING),
);
const babelLoaderItem = rule.use[babelLoaderIndex];
if (babelLoaderIndex !== -1) {
rule.use = [
...rule.use.slice(0, babelLoaderIndex),
babelLoaderItem,
makeLinariaLoaderConfig(babelLoaderItem.options),
...rule.use.slice(babelLoaderIndex + 2),
];
injectedBabelLoader = true;
}
} else if (
typeof rule.use.loader === 'string' &&
rule.use.loader.includes(BABEL_LOADER_STRING)
) {
rule.use = [rule.use, makeLinariaLoaderConfig(rule.use.options)];
injectedBabelLoader = true;
}
crossRules(Array.isArray(rule.use) ? rule.use : [rule.use]);
}
if (Array.isArray(rule.oneOf)) {
crossRules(rule.oneOf);
}
}
}
function withLinaria(nextConfig = {}) {
return {
...nextConfig,
webpack(config, options) {
crossRules(config.module.rules);
if (!injectedBabelLoader) {
config.module.rules.push({
test: /\.(tsx?|jsx?)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: linariaWebpackLoaderConfig.options.babelOptions,
},
linariaWebpackLoaderConfig,
],
});
}
if (typeof nextConfig.webpack === 'function') {
return nextConfig.webpack(config, options);
}
return config;
},
};
}
module.exports = withLinaria({ /* ... */ });
That whole crossRules
thing is a little messy but the point of it is to find Next's Babel rule, read the Babel options directly from it, and add the @linaria/webpack-loader
sequentially after the Babel one.
Also I'm not sure if the getLocalIdent
stuff is strictly necessary; I cargo-culted that in from next-linaria
.
I made a working example from @anulman 's comment. Thank you, really good work!
Of course I credited you in that repo!
https://github.com/LukasBombach/nextjs-linaria/tree/working-poc
Awww thanks @LukasBombach! In a recent project I had to rm @linaria
from the babel config; looks like some updates in the core libs auto-injects the Babel plugin or something
@LukasBombach Thanks for publishing a working example. But in case I use css module and linaria as well then some hydration issue is coming.
it seems that the culprit is rule.options.modules.exportOnlyLocals = true;
. exportOnlyLocals should be set to true only for server environment otherwise css file doesn't gets generated.
also, only having rule.options.modules.auto = true;
while updating css module config just works perfectly fine. I cleaned up most of the stuff and it's still working.
Here is my snippet
if (rule.options && rule.options.modules && typeof rule.options.modules.getLocalIdent === "function") { rule.options.modules.auto = true; }
Does the PR vercel/next.js#41085 by @ThePatriczek apply the same changes that are being proposed here?
Changed files: https://github.com/vercel/next.js/pull/41085/files
@jayu If the example has been updated, maybe this issue can be now closed?
cc @mikestopcontinues
Thanks @LukasBombach for the example. I got some issues while using configurations from your repo on Next 13.
For reproduction: I cloned your repo and upgraded the Next.js to version 13
$ yarn build
yarn run v1.22.19
$ next build
info - Linting and checking validity of types
info - Disabled SWC as replacement for Babel because of custom Babel configuration "babel.config.js" https://nextjs.org/docs/messages/swc-disabled
info - Using external babel configuration from /workspaces/nextjs-linaria/babel.config.js
info - Creating an optimized production build ..<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|/workspaces/nextjs-linaria/node_modules/next/dist/build/babel/loader/index.js??ruleSet[1].rules[3].oneOf[2].use[0]!/workspaces/nextjs-linaria/node_modules/@linaria/webpack-loader/lib/index.js??ruleSet[1].rules[3].oneOf[2].use[1]!/workspaces/nextjs-linaria/pages/_document.tsx': No serializer registered for ConfigError
<w> while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> webpack/lib/ModuleBuildError -> ConfigError
<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|/workspaces/nextjs-linaria/node_modules/next/dist/build/babel/loader/index.js??ruleSet[1].rules[3].oneOf[2].use[0]!/workspaces/nextjs-linaria/node_modules/@linaria/webpack-loader/lib/index.js??ruleSet[1].rules[3].oneOf[2].use[1]!/workspaces/nextjs-linaria/pages/index.tsx': No serializer registered for ConfigError
<w> while serializing webpack/lib/cache/PackFileCacheStrategy.PackContentItems -> webpack/lib/NormalModule -> webpack/lib/ModuleBuildError -> ConfigError
info - Creating an optimized production build
Failed to compile.
./pages/_document.tsx
Error: Unknown option: .hasServerComponents. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.
at validate (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/validation/options.js:92:25)
at loadPrivatePartialConfig (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/partial.js:80:50)
at loadPrivatePartialConfig.next (<anonymous>)
at loadFullConfig (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/full.js:57:46)
at loadFullConfig.next (<anonymous>)
at Function.<anonymous> (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/index.js:35:43)
at Generator.next (<anonymous>)
at evaluateSync (/workspaces/nextjs-linaria/node_modules/gensync/index.js:251:28)
at Function.sync (/workspaces/nextjs-linaria/node_modules/gensync/index.js:89:14)
at Object.<anonymous> (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/index.js:53:60)
./pages/index.tsx
Error: Unknown option: .hasServerComponents. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.
at validate (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/validation/options.js:92:25)
at loadPrivatePartialConfig (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/partial.js:80:50)
at loadPrivatePartialConfig.next (<anonymous>)
at loadFullConfig (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/full.js:57:46)
at loadFullConfig.next (<anonymous>)
at Function.<anonymous> (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/index.js:35:43)
at Generator.next (<anonymous>)
at evaluateSync (/workspaces/nextjs-linaria/node_modules/gensync/index.js:251:28)
at Function.sync (/workspaces/nextjs-linaria/node_modules/gensync/index.js:89:14)
at Object.<anonymous> (/workspaces/nextjs-linaria/node_modules/@babel/core/lib/config/index.js:53:60)
> Build failed because of webpack errors
error Command failed with exit code 1.
Adding a new option inside makeLinariaLoaderConfig solved the problem. The code would then look something like this.
...
const makeLinariaLoaderConfig = babelOptions => ({
loader: require.resolve("@linaria/webpack-loader"),
options: {
sourceMap: true,
extension: ".linaria.module.css",
babelOptions: _.omit(
babelOptions,
"isServer",
"distDir",
"pagesDir",
"development",
"hasReactRefresh",
"hasJsxRuntime",
"hasServerComponents"
),
},
});
...
Putting this here if anyone finds this useful: https://github.com/aaazureee/nextjs-linaria-purgecss
Hi @aaazureee, thanks for the example!
However, it looks like the example is using the old Pages Router (pages/
directory)
Would it be possible to switch this example to the App Router and show styling with React Server Components?
I also asked in https://github.com/aaazureee/nextjs-linaria-purgecss/issues/1
Describe the enhancement
After release of
2.0
with shaker as a default strategy, we should updatewith-linaria
example innext.js
repo. https://github.com/zeit/next.js/tree/master/examples/with-linariaMotivation
There is a known bug with next.js babel config and
extractor
evaluator, which can be fixed by theshaker
evaluator. See #447Possible implementations
open a PR to
next.js
with an update of linaria version