So I'm trying to build my Next.js project, which, for context, was being deployed without a problem, up until my main branch. When I then tried to implement mdx, with rehype and shiki, and tried to deploy that's what I got the errors. So trying to push the shiki-lol branch, that's when I get the error.
npm run dev works perfectly btw.
Deploying to Vercel or locally running npm run build, whichever I run, it doesn't work. I'm getting the following output:
> [email protected] build
> next build
▲ Next.js 15.2.1
Creating an optimized production build ...
Failed to compile.
HookWebpackError: Cannot read properties of undefined (reading '0')
at makeWebpackError (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:29:315788)
at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:29:106487
at eval (eval at create (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\webpack\bundle5.js:14:9224), <anonymous>:44:1)
TypeError: Cannot read properties of undefined (reading '0')
at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\static\css\f5a335e585685605.css:204:23247
at Parser.attribute (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84899)
at Parser.parse (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:99055)
at Parser.loop (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:98727)
at new Parser (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84494)
at Processor._root (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:101743)
at Processor._runSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:102250)
at Processor.processSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:103005)
at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:39339
at Array.every (<anonymous>)
at ensureCompatibility (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:38727)
caused by plugins in Compilation.hooks.processAssets
TypeError: Cannot read properties of undefined (reading '0')
at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\static\css\f5a335e585685605.css:204:23247
at Parser.attribute (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84899)
at Parser.parse (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:99055)
at Parser.loop (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:98727)
at new Parser (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:84494)
at Processor._root (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:101743)
at Processor._runSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:102250)
at Processor.processSync (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:103005)
at C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:39339
at Array.every (<anonymous>)
at ensureCompatibility (C:\Users\Santi\Documents\PERSONAL\code\WEB\santiu\node_modules\next\dist\compiled\cssnano-simple\index.js:320:38727)
It appears to be a problem with cssnano-simple, but I can't quite pinpoint why it's happening, anyone knows what the issue could be?? AI hasn't been useful either.
I will show relevant code snippets and package.json info, but regardless the full project is in the repo.
next.config.mjs
import nextMDX from '@next/mdx';
import createNextIntlPlugin from 'next-intl/plugin';
import process from 'node:process';
import rehypePrettyCode from 'rehype-pretty-code';
import remarkUnwrapImages from 'remark-unwrap-images';
Object.assign(process.env, { NEXT_TELEMETRY_DISABLED: '1' });
const plugins = [];
const nextConfig = {
reactStrictMode: true,
poweredByHeader: false,
experimental: {
optimizePackageImports: ['lucide-react', '@vercel/analytics'],
optimizeCss: false,
},
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
devIndicators: {
position: 'bottom-right',
},
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
minimumCacheTTL: 60,
dangerouslyAllowSVG: true,
contentDispositionType: 'attachment',
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
remotePatterns: [
{ protocol: 'https', hostname: 'via.placeholder.com' },
{ protocol: 'https', hostname: 'robohash.org' },
],
},
env: {
NEXT_TELEMETRY_DISABLED: '1',
},
};
/** @type {import('rehype-pretty-code').Options} */
const rehypePrettyCodeOptions = {
theme: 'catppuccin-macchiato',
keepBackground: false,
defaultLang: 'plaintext',
// Optimize performance
grid: false, // Disable grid layout for faster rendering
// Prevent lines from collapsing
onVisitLine(node) {
if (node.children.length === 0) {
node.children = [{ type: 'text', value: ' ' }];
}
},
// Add class to highlighted lines
onVisitHighlightedLine(node) {
if (!node.properties.className) {
node.properties.className = [];
}
node.properties.className.push('highlighted');
},
// Add class to highlighted words
onVisitHighlightedChars(node) {
node.properties.className = ['word'];
},
};
// Push MDX plugin
plugins.push(
nextMDX({
extension: /\.(md|mdx)$/,
options: {
remarkPlugins: [remarkUnwrapImages],
rehypePlugins: [[rehypePrettyCode, rehypePrettyCodeOptions]],
},
})
);
// Push next-intl plugin
plugins.push(createNextIntlPlugin());
export default () =>
plugins.reduce((config, plugin) => plugin(config), nextConfig);
package.json
{
"name": "santiu",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev ",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@heroicons/react": "^2.2.0",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "^16.0.1",
"@tailwindcss/typography": "^0.5.16",
"@types/mdx": "^2.0.13",
"@vercel/analytics": "^1.5.0",
"clsx": "^2.1.1",
"framer-motion": "^12.4.11",
"next": "^15.2.1",
"next-intl": "^4.0.1",
"next-themes": "^0.4.5",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"rehype-pretty-code": "^0.14.1",
"remark-unwrap-images": "^4.0.1",
"santiu": "file:",
"sharp": "^0.34.5",
"shiki": "^3.15.0",
"tailwind-merge": "^3.0.2"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"autoprefixer": "^10.4.21",
"eslint": "^9",
"eslint-config-next": "15.2.1",
"tailwindcss": "^4",
"typescript": "^5"
}
}
styles/globals.css
@import 'tailwindcss';
@layer base {
:root {
--background: #ffffff;
--foreground: #171717;
--foreground-rgb: 23, 23, 23;
color-scheme: dark;
}
[data-theme='dark'] {
--background: #171717;
--foreground: #fff;
--foreground-rgb: 237, 237, 237;
}
html {
@apply max-h-screen antialiased;
font-family: var(--font-inter), sans-serif;
background: var(--background);
color: var(--foreground);
transition: background-color 0.3s ease-in-out;
min-height: 100vh;
min-height: -webkit-fill-available;
will-change: background-color;
}
* {
box-sizing: border-box;
font-family: var(--font-inter), sans-serif;
}
body {
@apply transition-colors duration-300 m-0 p-0;
min-height: 100vh;
min-height: -webkit-fill-available;
font-family: var(--font-inter), sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Code font family */
code,
pre {
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', 'Liberation Mono',
'Courier New', monospace !important;
}
/* Control tab size */
pre {
tab-size: 2;
-moz-tab-size: 2;
@apply !px-0 rounded-lg;
}
code {
@apply text-sm md:text-base;
}
pre > code {
counter-reset: line;
}
/* Enhanced scrollbar for code blocks */
pre::-webkit-scrollbar {
height: 8px;
}
pre::-webkit-scrollbar-track {
background: #161b22;
}
pre::-webkit-scrollbar-thumb {
background: #30363d;
border-radius: 4px;
}
pre::-webkit-scrollbar-thumb:hover {
background: #484f58;
}
.capsize::before {
content: '';
margin-bottom: -0.098em;
display: table;
}
.capsize::after {
content: '';
margin-top: -0.219em;
display: table;
}
}
.font-menlo {
font-family: var(--font-menlo);
font-weight: 100;
text-transform: uppercase;
}
.font-menlo-thin {
font-family: var(--font-menlo);
font-weight: 400;
font-size: 0.825rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: rgba(var(--foreground-rgb), 0.55);
opacity: 0.9;
}
@layer components {
button {
@apply select-none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
}
}
@layer utilities {
.text-primary {
color: var(--foreground);
}
.text-secondary {
color: rgba(var(--foreground-rgb), 0.8);
}
.text-tertiary {
color: rgba(var(--foreground-rgb), 0.6);
}
.text-quaternary {
color: rgba(var(--foreground-rgb), 0.4);
}
}
/* MDX prose container */
.prose-mdx {
max-width: 78ch;
margin: 0 auto;
}
.prose {
@apply max-w-220 sm:text-lg md:text-xl !leading-7 sm:!leading-9;
}
/* ============================================ */
/* REHYPE-PRETTY-CODE THEME SUPPORT */
/* ============================================ */
/* Light/Dark theme handling with Shiki variables */
code[data-theme*=' '],
code[data-theme*=' '] span {
color: var(--shiki-light);
background-color: var(--shiki-light-bg);
}
@media (prefers-color-scheme: dark) {
code[data-theme*=' '],
code[data-theme*=' '] span {
color: var(--shiki-dark);
background-color: var(--shiki-dark-bg);
}
}
/* ============================================ */
/* CODE BLOCK STRUCTURE */
/* ============================================ */
/* Figure wrapper for code blocks */
figure[data-rehype-pretty-code-figure] {
@apply shadow-xl mb-6 mt-1 rounded-lg border border-neutral-700 overflow-hidden;
}
/* Pre styling */
figure[data-rehype-pretty-code-figure] pre {
@apply m-0 p-6 bg-neutral-800/50;
}
/* Main code block styling */
pre [data-line] {
@apply px-4 border-l-2 border-l-transparent;
line-height: 1.4; /* Control line spacing here too */
}
/* ============================================ */
/* LINE NUMBERS */
/* ============================================ */
code[data-line-numbers] {
counter-reset: line;
}
code[data-line-numbers] > [data-line]::before {
counter-increment: line;
content: counter(line);
@apply inline-block w-4 mr-4 text-right text-gray-500;
}
/* ============================================ */
/* HIGHLIGHTING */
/* ============================================ */
/* Highlighted lines */
[data-highlighted-line] {
background: rgba(200, 200, 255, 0.1);
@apply border-l-blue-400;
}
/* Highlighted characters/words */
[data-highlighted-chars] {
@apply bg-zinc-600/50 rounded;
box-shadow: 0 0 0 4px rgb(82 82 91 / 0.5);
}
/* Custom character highlighting with IDs */
[data-chars-id] {
@apply shadow-none p-1 border-b-2;
}
[data-chars-id] span {
@apply !text-inherit;
}
[data-chars-id='v'] {
@apply !text-pink-300 bg-rose-800/50 border-b-pink-600 font-bold;
}
[data-chars-id='s'] {
@apply !text-yellow-300 bg-yellow-800/50 border-b-yellow-600 font-bold;
}
[data-chars-id='i'] {
@apply !text-purple-200 bg-purple-800/50 border-b-purple-600 font-bold;
}
/* ============================================ */
/* CODE BLOCK TITLE */
/* ============================================ */
[data-rehype-pretty-code-title] {
@apply border-b border-neutral-700/20 bg-neutral-800/50 text-neutral-100 rounded-t-lg py-2 pb-2.5 pl-5 text-sm underline;
}
/* Remove top border radius from pre when title exists */
figure[data-rehype-pretty-code-figure]:has(> [data-rehype-pretty-code-title])
pre {
@apply !rounded-t-none;
}
/* ============================================ */
/* INLINE CODE */
/* ============================================ */
/* text-blue-300 */
:not(pre) > code {
@apply font-mono text-sm bg-neutral-800/70 text-blue-300 px-1.5 py-0.5 rounded border border-neutral-700/50;
}
/* h3 inline code sizing */
h3 code {
@apply !text-lg md:!text-xl;
}
/* ============================================ */
/* OVERFLOW HANDLING */
/* ============================================ */
pre,
code,
figure {
@apply overflow-x-auto;
}
/* p {
@apply text-xl;
}
article p {
@apply leading-9;
} */
[data-rehype-pretty-code-figure] span {
font-family: 'SF Mono', 'Monaco', 'Menlo', 'Consolas', 'Liberation Mono',
'Courier New', monospace !important;
@apply text-sm;
}
mdx-components.tsx
import type { MDXComponents } from 'mdx/types';
import Link from 'next/link';
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
h1: ({ children }) => (
<h1 className='text-lg font-bold text-neutral-100 leading-tight'>
{children}
</h1>
),
h2: ({ children }) => (
<h2 className='font-menlo-thin scroll-mt-20 !text-xs mt-30 mb-6 leading-tight'>
{children}
</h2>
),
p: ({ children }) => (
<p className='mb-6 leading-[1.75] text-base md:text-lg text-neutral-300'>
{children}
</p>
),
a: ({ href, children }) => {
if (href?.startsWith('http')) {
return (
<a
href={href}
className='text-blue-400 underline underline-offset-2 hover:text-blue-300 transition-colors'
target='_blank'
rel='noopener noreferrer'
>
{children}
</a>
);
}
return (
<Link
href={href || ''}
className='text-blue-400 underline underline-offset-2 hover:text-blue-300 transition-colors'
>
{children}
</Link>
);
},
ul: ({ children }) => (
<ul className='list-disc list-inside mb-6 space-y-2 text-base md:text-lg text-neutral-300'>
{children}
</ul>
),
ol: ({ children }) => (
<ol className='list-decimal list-inside mb-6 space-y-2 text-base md:text-lg text-neutral-300'>
{children}
</ol>
),
li: ({ children }) => <li className='leading-[1.75]'>{children}</li>,
blockquote: ({ children }) => (
<blockquote className='border-l-4 border-neutral-600 pl-6 py-2 my-6 italic text-neutral-400'>
{children}
</blockquote>
),
img: (props) => (
<figure className='my-12 -mx-4 md:-mx-12 lg:-mx-24'>
<img
{...props}
className='w-full h-auto rounded-none md:rounded-lg shadow-2xl'
loading='lazy'
/>
{props.alt && (
<figcaption className='text-center text-xs text-neutral-500 mt-4 px-4'>
{props.alt}
</figcaption>
)}
</figure>
),
hr: () => <hr className='my-12 border-t border-neutral-800' />,
...components,
};
}
bg-red-500!) instead of before it (!bg-red-500). Starting from TailwindCSS v4, you need to use@custom-variantfor thedark:variant. Dark and light modes can be handled like this: stackoverflow.com/a/79499827/15167500@utilitybetter than@layer utilities. However, for something liketext-primary, you don't need to create a utility - just define a new color in@theme,--color-primary: var(--foreground). I think you should read through the breaking changes documentation. -- What's breaking changes from v4 --- Useful references for v4