Skip to content

Commit 7196c7a

Browse files
authored
Fix syntax highlighter flicker by lazy loading theme and component (#1846)
Instead of just dynamically importing the theme and separately loading lazily the prism highlighter, we do it together and we avoid the flickering issue
1 parent d8245dc commit 7196c7a

File tree

3 files changed

+66
-59
lines changed

3 files changed

+66
-59
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use client'
2+
import { Prism as PrismHighlighter } from 'react-syntax-highlighter'
3+
import { CurrentTheme } from '../../../../../constants'
4+
import { ComponentProps, use } from 'react'
5+
import { cn } from '../../../../../lib/utils'
6+
7+
type Props = Omit<ComponentProps<typeof PrismHighlighter>, 'style'> & {
8+
currentTheme?: string
9+
}
10+
11+
const darkThemePromise = import(
12+
'react-syntax-highlighter/dist/esm/styles/prism/one-dark'
13+
).then((module) => module.default)
14+
15+
const lightThemePromise = import(
16+
'react-syntax-highlighter/dist/esm/styles/prism/one-light'
17+
).then((module) => module.default)
18+
19+
export default function LazyPrism({
20+
children,
21+
className,
22+
language,
23+
currentTheme,
24+
}: Props) {
25+
const style = use(
26+
currentTheme === CurrentTheme.Dark ? darkThemePromise : lightThemePromise,
27+
)
28+
29+
return (
30+
<PrismHighlighter
31+
language={language}
32+
style={style}
33+
className={cn('text-xs', className)}
34+
customStyle={{
35+
borderRadius: '0.375rem',
36+
padding: '1rem',
37+
lineHeight: '1.25rem',
38+
margin: '0',
39+
}}
40+
>
41+
{children}
42+
</PrismHighlighter>
43+
)
44+
}

packages/web-ui/src/ds/atoms/CodeBlock/SyntaxHightlighter/index.tsx

Lines changed: 11 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,29 @@
11
'use client'
22

3-
import React, {
4-
lazy,
5-
ComponentProps,
6-
Suspense,
7-
useEffect,
8-
useState,
9-
useMemo,
10-
} from 'react'
11-
import { cn } from '../../../../lib/utils'
12-
import { CurrentTheme } from '../../../../constants'
3+
import { ComponentProps, Suspense, lazy } from 'react'
4+
import { Prism as PrismHighlighter } from 'react-syntax-highlighter'
5+
import { Skeleton } from '../../Skeleton'
136

14-
const LazyPrism = lazy(() =>
15-
import('react-syntax-highlighter').then((mod) => ({ default: mod.Prism })),
16-
)
17-
18-
type PrismThemeStyle = { [key: string]: React.CSSProperties } | undefined
19-
20-
type Props = Omit<ComponentProps<typeof LazyPrism>, 'style'> & {
21-
currentTheme: string | undefined
7+
type Props = Omit<ComponentProps<typeof PrismHighlighter>, 'style'> & {
8+
currentTheme?: string
229
}
2310

11+
const LazyPrism = lazy(() => import('./LazyPrism'))
12+
2413
export function SyntaxHighlighter({
2514
children,
2615
className,
2716
language,
2817
currentTheme,
2918
}: Props) {
30-
const [style, setStyle] = useState<PrismThemeStyle>(undefined)
31-
useEffect(() => {
32-
let active = true
33-
34-
const loadTheme = async () => {
35-
let themeModule
36-
if (currentTheme === CurrentTheme.Dark) {
37-
themeModule = await import(
38-
'react-syntax-highlighter/dist/esm/styles/prism/one-dark'
39-
)
40-
} else {
41-
themeModule = await import(
42-
'react-syntax-highlighter/dist/esm/styles/prism/one-light'
43-
)
44-
}
45-
46-
if (active) {
47-
setStyle(themeModule.default)
48-
}
49-
}
50-
loadTheme()
51-
52-
return () => {
53-
active = false
54-
}
55-
}, [currentTheme])
56-
5719
return (
58-
<Suspense fallback={<div className='p-4 text-sm'>Loading code...</div>}>
20+
<Suspense fallback={<Skeleton height='h5' />}>
5921
<LazyPrism
60-
className={cn('text-xs', className)}
22+
className={className}
6123
language={language}
62-
style={style}
63-
customStyle={{
64-
borderRadius: '0.375rem',
65-
padding: '1rem',
66-
lineHeight: '1.25rem',
67-
margin: '0',
68-
}}
24+
currentTheme={currentTheme}
6925
>
70-
{useMemo(() => children, [children])}
26+
{children}
7127
</LazyPrism>
7228
</Suspense>
7329
)

packages/web-ui/src/ds/atoms/CodeBlock/index.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { useTheme } from 'next-themes'
4-
import { memo, ReactNode } from 'react'
4+
import { useMemo, memo, ReactNode } from 'react'
55

66
import { CurrentTheme } from '../../../constants'
77
import { cn } from '../../../lib/utils'
@@ -19,8 +19,10 @@ interface CodeBlockProps {
1919
bgColor?: string
2020
}
2121

22-
export function useCodeBlockBackgroundColor(override?: string) {
23-
const { resolvedTheme } = useTheme()
22+
function getCodeBlockBackgroundColor(
23+
override?: string,
24+
resolvedTheme?: string,
25+
) {
2426
if (override) return override
2527
if (resolvedTheme === CurrentTheme.Light) return 'bg-backgroundCode'
2628
return 'bg-[#282c34]'
@@ -37,7 +39,12 @@ const Content = memo(
3739
bgColor: overrideBgColor,
3840
}: CodeBlockProps) => {
3941
const { resolvedTheme } = useTheme()
40-
const bgColor = useCodeBlockBackgroundColor(overrideBgColor)
42+
43+
const bgColor = useMemo(
44+
() => getCodeBlockBackgroundColor(overrideBgColor, resolvedTheme),
45+
[overrideBgColor, resolvedTheme],
46+
)
47+
4148
return (
4249
<div
4350
className={cn(

0 commit comments

Comments
 (0)