# Build a simple, isolated demo component with Gatsby

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.mdx` file.
- 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.mdx
```

## Importing 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`](https://webpack.js.org/loaders/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:

```shell
npm i --save-dev html-loader
```

Then export the
[`onCreateWebpackConfig` hook](https://www.gatsbyjs.com/docs/how-to/custom-configuration/add-custom-webpack-config/#examples)
from the `gatsby-node.js` file, like so:

```js
// 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:

```html
<!-- 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:

[Interactive example](/simple-demo)

There you have it. Check my post on how to make a
[React iframe component](/iframe-react-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](https://www.gatsbyjs.com/docs/how-to/images-and-media/static-folder/), 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.
