Adding Interactive Code Demos to My Next.js Blog with Sandpack
I wanted to include live, editable code examples in my blog posts without the hassle of maintaining separate CodeSandbox links. Sandpack solved this perfectly by bringing the CodeSandbox experience directly into my MDX posts.
The Setup
First, I installed Sandpack from npm. Information about Sandpack is available here
I created a reusable Sandbox
component that wraps Sandpack with sensible defaults for my R3F demos:
// components/r3f/Sandbox.jsx
import { Sandpack } from "@codesandbox/sandpack-react";
export default async function Sandbox({ filePath, ...props }) {
let defaults = {
template: "react",
theme: "dark",
options: {
editorHeight: 500
},
customSetup: {
dependencies: {
"three": "^0.178.0",
"@react-three/fiber": "^9.3.0",
"@react-three/drei": "^10.6.1",
"leva": "^0.10.0",
},
}
};
const config = { ...defaults, ...props }
// File loading logic here...
return <Sandpack {...config} />
}
File Loading Feature
One cool feature I added was automatic file loading to avoid having to duplicate code. Instead of copying code into markdown files, I can reference external files and run them in the sandbox:
export function getComponentCode(componentPath) {
const fullPath = path.join(process.cwd(), componentPath);
return fs.readFileSync(fullPath, 'utf8');
}
If a filePath
prop is provided, the component loads the file and injects it as /App.js
in the sandbox. This keeps my blog posts clean and lets me maintain the actual demo code separately.
MDX Integration
To use the component in MDX, I registered it in mdx-components.tsx
:
import Sandbox from "./components/r3f/Sandbox";
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
Sandbox: Sandbox,
...components,
};
}
Now I can drop interactive demos into any blog post:
<Sandbox filePath="demos/some-demo.jsx" />
Why I Like this
Clean separation: Demo code lives in separate files, blog posts stay readable.
Consistent setup: All my R3F demos get the same dependencies and configuration automatically.
Flexible override: I can still pass custom files or configurations when needed.
Zero maintenance: No external links to break or sandboxes to update separately.
Here is an example of the component in action:
Jeremy Atkinson
Jeremy is a structural engineer, researcher, and developer from BC. He works on Calcs.app and writes at Kinson.io