Skip to content

Instantly share code, notes, and snippets.

@codemile
Created August 20, 2023 14:05
Show Gist options
  • Save codemile/0a178292d6ac466350022bcb3306f740 to your computer and use it in GitHub Desktop.
Save codemile/0a178292d6ac466350022bcb3306f740 to your computer and use it in GitHub Desktop.
Emotion provider for MUI that works in layout.tsx files of the app directory in NextJs 13
'use client';
import createCache from '@emotion/cache';
import {CacheProvider, EmotionCache} from '@emotion/react';
import {
SerializedStyles,
StyleSheet
} from '@emotion/utils/dist/declarations/types';
import {useServerInsertedHTML} from 'next/navigation';
import {FC, PropsWithChildren, useCallback, useMemo} from 'react';
interface CachedStyle {
isGlobal: boolean;
name: string;
}
export interface ScaffoldEmotionCache extends EmotionCache {
__newStyles: Array<CachedStyle>;
__preInsert(
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
): string | void;
__flush(): Array<CachedStyle>;
}
const createCompactCache = (): ScaffoldEmotionCache => {
const cache: ScaffoldEmotionCache = createCache({
key: 'mui'
}) as ScaffoldEmotionCache;
cache.compat = true;
cache.__newStyles = [];
cache.__preInsert = cache.insert.bind(cache);
cache.__flush = () => {
const newStyles = cache.__newStyles.slice();
cache.__newStyles = [];
return newStyles;
};
cache.insert = (
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
) => {
if (cache.inserted[serialized.name] === undefined) {
cache.__newStyles.push({
name: serialized.name,
isGlobal: !selector
});
}
return cache.__preInsert(selector, serialized, sheet, shouldCache);
};
return cache;
};
export const ScaffoldEmotionCacheProvider: FC<PropsWithChildren> = ({
children
}) => {
const cache = useMemo(() => createCompactCache(), []);
const serverInsertedHTML = useCallback(() => {
const inserted = cache.__flush();
if (inserted.length === 0) {
return null;
}
let styles = '';
let dataEmotionAttribute = cache.key;
const globals: Array<{name: string; style: string}> = [];
inserted.forEach(({name, isGlobal}) => {
const style = cache.inserted[name];
if (typeof style === 'string') {
if (isGlobal) {
globals.push({name, style});
} else {
styles += style;
dataEmotionAttribute += ` ${name}`;
}
}
});
return (
<>
{globals.map(({name, style}) => (
<style
key={name}
data-ssr="true"
data-emotion={`${cache.key}-global ${name}`}
dangerouslySetInnerHTML={{__html: style}}
/>
))}
{Boolean(styles) && (
<style
data-ssr="true"
data-emotion={dataEmotionAttribute}
dangerouslySetInnerHTML={{__html: styles}}
/>
)}
</>
);
}, [cache]);
useServerInsertedHTML(serverInsertedHTML);
return <CacheProvider value={cache}>{children}</CacheProvider>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment