Here’s how to build a really simple code demo iframe with Gatsby, which lives outside the site’s main CSS scope.
The nice thing about static site generators like Gatsby is that you can use MDX to write post, which means you can write demos right into the posts with React components. That’s great, but sometimes you want a demo that lives outside the current scope. That can be done with an iframe.
Some requirements we want our demo to meet:
- All files should be in the same dir with the main
index.mdxfile. - The demo is just one file.
- No build process.
The dir structure looks something like this:
posts/
├── code-demo/
│ ├── demo.html
│ └── index.mdx
├── bar-foo.mdxImporting HTML as a string
It would be cool if Gatsby would render HTML files placed into the post dir:
posts/code-demo/demo.html, we could then load them into an iframe:
<iframe src='/code-demo/demo.html'/>. But it doesn’t do that, which is okay,
because iframe also has the lesser known attribute srcdoc, which you can
pass an HTML document as a string and it will just render it normally. Neat.
Use WebPack html-loader with Gatsby
We need to use WebPack’s
html-loader to import HTML
files. This is pretty easily done in Gatsby, since Gatsby is essentially a big
WebPack config system.
First install it:
npm i --save-dev html-loaderThen export the
onCreateWebpackConfig hook
from the gatsby-node.js file, like so:
// gatsby-node.js
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
module: {
rules: [
{
test: /\.html$/i,
loader: 'html-loader',
},
],
},
})
}Pass HTML as a string into an iframe
Our simple test demo file looks something like this, just a good old HTML file:
<!-- posts/code-demo/demo.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Test demo</title>
<style id="styles">
#foo {
margin-bottom: 10px;
}
.hidden {
visibility: hidden;
}
</style>
</head>
<body>
<button id="foo">Button</button>
<div id="message" class="hidden">💥</div>
</body>
<script id="scripts">
const button = document.getElementById('foo')
const message = document.getElementById('message')
button.addEventListener('click', () => message.classList.toggle('hidden'))
</script>
</html>Then import it in the MDX post file and put the string into an iframe:
---
title: Code demo
date: 2021-12-28 21:26:44
---
import demo from './demo.html'
Here’s the demo:
<iframe srcdoc={demo} title='Code demo' />And it renders like this:
There you have it. Check my post on how to make a React iframe component.
Downsides to this approach
It’s just HTML, any imports are not resolved. The
ESM import statement works in modern
browsers tho, but if you try to do import foo from './foo', it looks from
/foo.js, that would be the static
dir, not
here in the post dir. But script tags can be used like normal for sure.
Conclusions
I’m planing a post on more elaborate demo system. Probably publishing it within next few days.