Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update with-linaria example in next.js repo #589

Open
jayu opened this issue Apr 28, 2020 · 27 comments
Open

Update with-linaria example in next.js repo #589

jayu opened this issue Apr 28, 2020 · 27 comments
Labels
cat: documentation 📖 Work focused on writing documentation good first issue 😊 It is a good issue for new comers platform: next.js 🛠️ Issue related to next.js skip autolabeling Use it to disable auto laberer bot for an issue/pr
Milestone

Comments

@jayu
Copy link
Contributor

jayu commented Apr 28, 2020

Describe the enhancement

After release of 2.0 with shaker as a default strategy, we should update with-linaria example in next.js repo. https://github.com/zeit/next.js/tree/master/examples/with-linaria

Motivation

There is a known bug with next.js babel config and extractor evaluator, which can be fixed by the shaker evaluator. See #447

Possible implementations

open a PR to next.js with an update of linaria version

@jayu jayu added good first issue 😊 It is a good issue for new comers cat: documentation 📖 Work focused on writing documentation platform: next.js 🛠️ Issue related to next.js skip autolabeling Use it to disable auto laberer bot for an issue/pr labels Apr 28, 2020
@jayu jayu added this to the 2.0.0 release milestone Apr 28, 2020
@jayu jayu changed the title Update with-linaria example in next repo Update with-linaria example in next.js repo Apr 28, 2020
@lifeiscontent
Copy link

@jayu any ideas when this might be updated?

@anulman
Copy link

anulman commented May 27, 2020

@lifeiscontent Not a maintainer, but here are the instructions for modern Next.js: #447

@marbiano
Copy link

Done: vercel/next.js#13568

@chrisands
Copy link

chrisands commented Jun 20, 2020

How to use linaria css api (not styled) with nextjs?
It tries to inject files but got nextjs error

@jayu
Copy link
Contributor Author

jayu commented Jun 22, 2020

@chrisands can you spin up a repo with reproduction and open a new issue for that?

@chrisands
Copy link

@jayu I'll try to do this week

@switz
Copy link

switz commented Jun 29, 2020

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',
        },
    ],
};

@Mistereo
Copy link
Contributor

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?

@nickrttn
Copy link

@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.

@joyfulelement
Copy link

@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.

@steveadams
Copy link

steveadams commented Jan 6, 2021

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.

@nickrttn
Copy link

nickrttn commented Jan 6, 2021

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 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?

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.

@turadg
Copy link
Contributor

turadg commented Mar 26, 2021

FWIW, with-linaria uses next-linaria since vercel/next.js#23332

Of course :global() still doesn't work. Anyone have ideas for solving that?

@M1r1k
Copy link

M1r1k commented Jun 10, 2021

Here is the fix, that I believe will be in next release if next-linaria - #724 (comment)

@mikestopcontinues
Copy link
Contributor

@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

@huydq-1218
Copy link

huydq-1218 commented Jul 12, 2022

Anyone have a working example with NextJS 12 with Typescript and linaria 3 beta?

@jonataslaw
Copy link

Linaria 4 no longer works with Nextjs 12.
At least not the dynamic properties.
Version 2 works normally.

@anulman
Copy link

anulman commented Aug 20, 2022

I got this working with NextJS 12 & Linaria 4. Just walking back through my changes; will publish something shortly

@fandy
Copy link

fandy commented Aug 23, 2022

@anulman can you push up an example for us?

@anulman
Copy link

anulman commented Aug 23, 2022

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.

@LukasBombach
Copy link

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

@anulman
Copy link

anulman commented Oct 6, 2022

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

@Nandan1996
Copy link

Nandan1996 commented Oct 9, 2022

@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; }

@karlhorky
Copy link

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

@frenicohansen
Copy link

frenicohansen commented Dec 14, 2022

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.

EDIT

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"
    ),
  },
});

...

@aaazureee
Copy link

aaazureee commented Jun 24, 2023

Putting this here if anyone finds this useful: https://github.com/aaazureee/nextjs-linaria-purgecss

@karlhorky
Copy link

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 aaazureee/nextjs-linaria-purgecss#1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cat: documentation 📖 Work focused on writing documentation good first issue 😊 It is a good issue for new comers platform: next.js 🛠️ Issue related to next.js skip autolabeling Use it to disable auto laberer bot for an issue/pr
Projects
None yet
Development

No branches or pull requests