Created
August 20, 2023 14:05
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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