Skip to content

Commit

Permalink
feat: add emotion cache
Browse files Browse the repository at this point in the history
  • Loading branch information
orafaelfragoso committed Dec 6, 2023
1 parent 5d7729e commit 234ae2a
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use client';
import * as React from 'react';
import createCache from '@emotion/cache';
import { useServerInsertedHTML } from 'next/navigation';
import { CacheProvider as DefaultCacheProvider } from '@emotion/react';
import type { EmotionCache, Options as OptionsOfCreateCache } from '@emotion/cache';

export type NextAppDirEmotionCacheProviderProps = {
/** This is the options passed to createCache() from 'import createCache from "@emotion/cache"' */
options: Omit<OptionsOfCreateCache, 'insertionPoint'>;
/** By default <CacheProvider /> from 'import { CacheProvider } from "@emotion/react"' */
CacheProvider?: (props: {
value: EmotionCache;
children: React.ReactNode;
}) => React.JSX.Element | null;
children: React.ReactNode;
};

// Adapted from https://github.com/garronej/tss-react/blob/main/src/next/appDir.tsx
export default function NextAppDirEmotionCacheProvider(props: NextAppDirEmotionCacheProviderProps) {
const { options, CacheProvider = DefaultCacheProvider, children } = props;

const [registry] = React.useState(() => {
const cache = createCache(options);
cache.compat = true;
const prevInsert = cache.insert;
let inserted: { name: string; isGlobal: boolean }[] = [];
cache.insert = (...args) => {
const [selector, serialized] = args;
if (cache.inserted[serialized.name] === undefined) {
inserted.push({
name: serialized.name,
isGlobal: !selector,
});
}
return prevInsert(...args);
};
const flush = () => {
const prevInserted = inserted;
inserted = [];
return prevInserted;
};
return { cache, flush };
});

useServerInsertedHTML(() => {
const inserted = registry.flush();
if (inserted.length === 0) {
return null;
}
let styles = '';
let dataEmotionAttribute = registry.cache.key;

const globals: {
name: string;
style: string;
}[] = [];

inserted.forEach(({ name, isGlobal }) => {
const style = registry.cache.inserted[name];

if (typeof style !== 'boolean') {
if (isGlobal) {
globals.push({ name, style });
} else {
styles += style;
dataEmotionAttribute += ` ${name}`;
}
}
});

return (
<React.Fragment>
{globals.map(({ name, style }) => (
<style
key={name}
data-emotion={`${registry.cache.key}-global ${name}`}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style }}
/>
))}
{styles && (
<style
data-emotion={dataEmotionAttribute}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: styles }}
/>
)}
</React.Fragment>
);
});

return <CacheProvider value={registry.cache}>{children}</CacheProvider>;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import * as React from 'react';
import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';

import NextAppDirEmotionCacheProvider from '@/components/emotion-cache';
import theme from '@/lib/theme';

export type MuiProviderProps = {
Expand All @@ -12,11 +13,11 @@ export type MuiProviderProps = {

export function MuiProvider({ children }: MuiProviderProps) {
return (
<StyledEngineProvider injectFirst>
<NextAppDirEmotionCacheProvider options={{ key: 'mui' }}>
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</StyledEngineProvider>
</NextAppDirEmotionCacheProvider>
);
}

0 comments on commit 234ae2a

Please sign in to comment.