-
-
Save colinhacks/c40519a6a050a99091862319151377ec to your computer and use it in GitHub Desktop.
| // ⚠️ works with Emotion 10 only! ⚠️ | |
| // 1. `yarn add emotion-server` | |
| // 2. copy the contents of this file into your `pages` directory | |
| // 3. save it as `_document.tsx` | |
| // should work out of the box | |
| import Document, { Head, Main, NextScript } from 'next/document'; | |
| import { extractCritical } from 'emotion-server'; | |
| export default class MyDocument extends Document { | |
| static async getInitialProps(ctx: any) { | |
| const initialProps = await Document.getInitialProps(ctx); | |
| const styles = extractCritical(initialProps.html); | |
| return { | |
| ...initialProps, | |
| styles: ( | |
| <> | |
| {initialProps.styles} | |
| <style | |
| data-emotion-css={styles.ids.join(' ')} | |
| dangerouslySetInnerHTML={{ __html: styles.css }} | |
| /> | |
| </> | |
| ), | |
| }; | |
| } | |
| render() { | |
| return ( | |
| <html> | |
| <Head /> | |
| <body> | |
| <Main /> | |
| <NextScript /> | |
| </body> | |
| </html> | |
| ); | |
| } | |
| } |
Hi @colinhacks, came from your article on
@emotion/corevs Vanilla Emotion.This is interesting to me too: it would be nice to have students at @upleveled avoid the footgun of using the
cssprop on an<a>inside of a Next.js<Link>and then thehrefnot being passed (see the problem at this CodeSandbox)I am not sure this is up to date with Emotion 11 though - do you have a more current version of this? For example, when I look at the version on the Emotion docs, it imports
createEmotionServerfrom@emotion/server/create-instanceand then creates a server, before finally destructuringextractCriticalout of it.Oh and if you do have a version, does it also use a function component instead of a class component (since Next.js supports this now)?
Full disclosure:
I am brand new to NextJS and Emotion. These seem relevant - but take them with a grain of salt.
@karlhorky, I thought this might interest you
https://emotion.sh/docs/ssr

The sentence I've highlighted seems misleading or possibly incomplete. I assume they mean emotion-server (pre Emotion 11) or @emotion/server (Emotion 11). But maybe perhaps I'm wrong.
Also check out their Emotion 11 notes here: https://emotion.sh/docs/emotion-11
@bcourtney5965 Thanks for the response :)
The highlighted sentence applies to @emotion/core (actually now called @emotion/react in v11).
I worked on the current official example in the Next.js repo to bring this up to date:
What this Gist is talking about is Vanilla emotion - which is a completely different animal :)
@karlhorky you're totally right, this is for Emotion 10 specifically. I'll update my blog post to indicate that. I don't have upgraded versions at this time, i'll try to get around to it soon though.
Ok great, thanks!
Ah I see, thank you karlhorky for the clarification. And thank you colinhacks for the writeup.
In latest Nextjs i have this that seems to work for me - typescript version. emotion >= 11
yarn add @emotion/server- copy the contents of this file into your
pagesdirectory - save it as
_document.tsx
_document.tsx
import * as React from 'react';
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from 'next/document';
import { extractCritical } from '@emotion/server';
export default class AppDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): ReturnType<typeof Document.getInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
const styles = extractCritical(initialProps.html);
return {
...initialProps,
styles: (
<React.Fragment>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(' ')}
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</React.Fragment>
),
};
}
render(): JSX.Element {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}yarn add @emotion/css @emotion/react- copy the contents of this file into your
pagesdirectory - save it as
_app.tsx
_app.tsx
import * as React from 'react';
import { cache } from '@emotion/css';
import { CacheProvider } from '@emotion/react';
import type { AppProps } from 'next/app';
function App(props: AppProps): JSX.Element {
const { Component, pageProps } = props;
return (
<CacheProvider value={cache}>
<Component {...pageProps} />
</CacheProvider>
);
}
export default App;Hope that helps
Very cool @MMT-LD! 💯 Maybe the Next.js team would actually accept this as a further example in the /examples directory - maybe called with-emotion-vanilla or with-vanilla-emotion - to provide an alternative to the with-emotion example.
cc @lfades - this would be an example of how to use Next.js with vanilla Emotion (as opposed to @emotion/react
Diclaimer - not fully tested this its rough but think it'll work.
Another way to get this to work with the custom way (if you want a custom config) would be...
shared/emotion.ts
import createEmotion from '@emotion/css/create-instance';
export const {
flush,
hydrate,
cx,
getRegisteredStyles,
injectGlobal,
keyframes,
css,
cache,
sheet,
} = createEmotion({ key: 'css-whatever' });_document.tsx - note the new import shared/emotion and @emotion/server/create-instance
import * as React from 'react';
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import { cache } from 'shared/emotion';
const { extractCritical } = createEmotionServer(cache);
export default class AppDocument extends Document {
static async getInitialProps(
ctx: DocumentContext
): ReturnType<typeof Document.getInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
const styles = extractCritical(initialProps.html);
return {
...initialProps,
styles: (
<React.Fragment>
{initialProps.styles}
<style
data-emotion-css={styles.ids.join(' ')}
dangerouslySetInnerHTML={{ __html: styles.css }}
/>
</React.Fragment>
),
};
}
render(): JSX.Element {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}_app.tsx - note the new import shared/emotion
import * as React from 'react';
import { cache } from 'shared/emotion';
import { CacheProvider } from '@emotion/react';
import type { AppProps } from 'next/app';
function App(props: AppProps): JSX.Element {
const { Component, pageProps } = props;
return (
<CacheProvider value={cache}>
<Component {...pageProps} />
</CacheProvider>
);
}
export default App;Doing it this way is a little maintenance, however, more flexible. All that is required is to use the css, keyframes etc etc from shared/emotion rather than emotion css. When styling your app. The css classname should now have classname='css-whatever-randomstring'. For example,
import { css } from 'shared/emotion';
const style = css({ color: 'white', padding: 0, background: 'red' });
<div className={style}>cool</div>Nice, thanks!
I've opened a Next.js issue to see if they are interested in this: vercel/next.js#20199
Hi @colinhacks, came from your article on
@emotion/corevs Vanilla Emotion.This is interesting to me too: it would be nice to have students at @upleveled avoid the footgun of using the
cssprop on an<a>inside of a Next.js<Link>and then thehrefnot being passed (see the problem at this CodeSandbox)I am not sure this is up to date with Emotion 11 though - do you have a more current version of this? For example, when I look at the version on the Emotion docs, it imports
createEmotionServerfrom@emotion/server/create-instanceand then creates a server, before finally destructuringextractCriticalout of it.Oh and if you do have a version, does it also use a function component instead of a class component (since Next.js supports this now)?