To make your code editor's Preview area:
- Avoid
eval()
!
Pass instead the JavaScript code string to Function like: new Function(codeString)();
in order to run it.
- use a more secure context such as a Sandboxed iframe with flags
<iframe id="iframe" src="preview.html" sandbox="allow-same-origin allow-scripts allow-modals allow-forms" frameborder="0"></iframe>
- Given the iframe is an isolated context, the styles you enter within your code editor will not destroy the default styles of the host app. I.e: imagine someone doing: CSS
textarea {display: none;}
.
Basically you'll need two files: index.html, preview.html (for the iframe) and use PostMessage API to communicate between the index.html host app and the preview.html file:
Since we cannot use postMessage in the sandboxed (as well :)) Stack Overflow snippets environment, here's a quick gif demo:

index.html
Your main application page with the HTML, CSS and JS textareas and an iframe for the Preview area
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Code Editor - App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* { margin:0; box-sizing:border-box; }
body { padding:10px; }
iframe, textarea { width:100%; min-height:3em; }
</style>
</head>
<body>
<textarea id="html">Hello, stack <b>overflow!</b></textarea>
<textarea id="css">body {background: #F48024;}</textarea>
<textarea id="js">document.body.innerHTML += "<b>World!</b>";</textarea>
<iframe id="preview" src="preview.html" sandbox="allow-same-origin allow-scripts allow-modals allow-forms" frameborder="0"></iframe>
<script>
const textareas = document.querySelectorAll("#css, #html, #js");
const preview = document.querySelector("#preview");
const post = () => textareas.forEach(el => preview.contentWindow.postMessage({
id: el.id,
value: el.value
}, '*')); // Use 'http://example.com' instead of '*' as your targetOrigin
// Setup Events and Iframe Init
textareas.forEach(el => el.addEventListener('input', post));
preview.addEventListener('load', post);
</script>
</body>
</html>
in the code above, make sure to use your exact domain for targetOrigin instead of *
- for additional security.
preview.html
This is the Preview area, actually a brand clean Document which is called into the sandboxed iframe.
Its elements #__app__html
, #__app__css
and #__app__js
will be populated by the strings received from the PostMessage API from the host index.html
document:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Preview</title>
<style id="__app__css"></style>
<script id="__app__js"></script>
<script>
window.addEventListener('message', (evt) => {
// if (evt.origin !== "http://example.com:8080") return; // Fix and uncomment in production
document.getElementById(`__app__${evt.data.id}`).innerHTML = evt.data.value;
if (evt.data.id==='js') new Function(evt.data.value)();
});
</script>
</head>
<body id="__app__html"></body>
</html>
Make sure to uncomment the if (evt.origin
line for production.