Editing the Next.js Root Div
If you've done any development with next.js then you have no doubtably seen the root div on the page.
<div id="__next">
/* your app */
</div>
I'm working on a project where I needed to add a class to this div. Turns out there's two ways to interact with this element.
useEffect
The easy way is to add a useEffect hook to _app.js.
function MyApp({ Component, pageProps }) {
useEffect(() => {
document.getElementById("__next").className = "custom-class-name";
}, []);
return <Component {...pageProps} />;
}
export default MyApp;
Override <Main />
Option 1, while easy and straightforward, is very boring. And honestly, it wasn't the first thought I had. I'm using a custom document which looks more or less like
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
What's this <Main />
component? Turns out it's a pretty simple and can be found in the next source here.
export function Main() {
const { inAmpMode, html, docComponentsRendered } = useContext(
DocumentComponentContext
)
docComponentsRendered.Main = true
if (inAmpMode) return <>{AMP_RENDER_TARGET}</>
return <div id="__next" dangerouslySetInnerHTML={{ __html: html }} />
}
And while I'm not going to pretend that I understand what it's doing – it's pretty obvious that there's the __next
element that I want to edit. Seems logical that if I replace <Main />
with my own version that includes a classname, should work.
It took some digging to find out the correct file that exports DocumentComponentContext
, but after that worked like a charm.
const DocumentContext = require("next/dist/next-server/lib/document-context")
.DocumentContext;
function MainOverride() {
const { inAmpMode, html, docComponentsRendered } = useContext(
DocumentContext
);
docComponentsRendered.Main = true;
if (inAmpMode) return <>{AMP_RENDER_TARGET}</>;
return <div id="__next" className="custom-class" dangerouslySetInnerHTML={{ __html: html }} />;
}
The render
function in the custom document file becomes
render() {
return (
<Html>
<Head />
<body>
<MainOverride />
<NextScript />
</body>
</Html>
)
}
While option 2 was more exciting to implement, I do not know if it's the wisest move. Since I'm importing directly from the next dist files, a future release could alter the underlying mechanism and break the app.
Until then, Happy coding.