Getting Started
Initialising Karagöz Sandbox involves a few steps. We will go through the basic steps in this guide.
Heads up!
Unless otherwise mentioned, all imports come from @karagoz/sandbox
.
Create File System Tree
Start by creating the file structure you want to mount to the WebContainer.
In this example we will create a simple express server that provides an endpoint under api/message
, as well as,
serves the static files under the public
directory at the server root.
The contents of each file will be created as a separate string for better readability.
This will be a relatively long chunk of code, so bear with me ^^
/**
* package.json
* Defines the dependencies to be installed and script to start the server.
* The server options indicate that it should restart when JS, HTML or CSS files change.
*/
const packageJson = `
{
"name": "example-app",
"type": "module",
"dependencies": {
"express": "latest",
"nodemon": "latest"
},
"scripts": {
"start": "nodemon --watch './' -e js,html,css server.js"
}
}
`
/**
* server.js
* Configures a web server on port 3111.
* Defines the endpoint /api/message that returns a string message.
* Serves the files in /public under the root path /.
*/
const serverJs = `
import express from 'express'
const app = express()
const port = 3111
app.get('/api/message', (req, res) => {
setTimeout(() => res.send('Welcome to a WebContainers app! 🥳'), 1000)
})
app.use('/', express.static('public'))
app.listen(port, () => {
console.log('App is live at http://localhost:' + port)
})
`
/**
* public/index.html
* The home page of the server.
* Links style.css for styling.
* Embeds script.js and calls the doSomething() function defined within it.
* IMPORTANT: we're escpaing the script closing tags, otherwise we will get errors.
*/
const indexHtml = `
<html>
<head>
<link rel="stylesheet" href="./style.css"/>
</head>
<body>
<h1>Home Page</h1>
<p class="response">Fetching message...</p>
<script src="./script.js"><${'/script'}>
<script>doSomething()<${'/script'}>
</body>
</html>
`
/**
* public/script.js
* Defines the function doSomething() that calls the API endpoint /api/messsage
* and injects the response into the div element with the class "response".
*/
const scriptJs = `
function doSomething() {
fetch('/api/message')
.then((response) => response.text())
.then((data) => (document.querySelector('.response').innerHTML = data))
}
`
/**
* public/style.css
* Basic styles.
*/
const styleCss = `
body {
font-family: Arial, Helvetica, sans-serif;
}
h1 {
color: #99cc33;
}
`
We will now assemble all that into a FileSystemTree
instance.
import type { FileSystemTree } from '@webcontainer/api'
/**
* Notice how we define files and directories.
*/
const fileTree: FileSystemTree = {
'server.js': { file: { contents: serverJs } },
'package.json': { file: { contents: packageJson } },
public: {
directory: {
'index.html': { file: { contents: indexHtml } },
'script.js': { file: { contents: scriptJs } },
'style.css': { file: { contents: styleCss } },
},
},
}
Create and Provide a WebContainer Instance
Now, import and call useSandboxBoot()
. It is a wrapper around WebContainer.boot()
and optionally takes a similar
BootOptions
instance as an argument.
After that, provide the resulting promise through provideWebContainer()
.
Then create the sandbox with useSandbox()
.
// `boot` is the promise returned by WebContainer.boot().
// `isBooting` is a convenience boolean flag that will be used later to indicate that the sandbox is still booting.
const { boot, isBooting } = useSandboxBoot()
// Sub-components and underlying code will await `boot` and use the resulting WebContainer instance.
provideWebContainer(boot)
// Must come after provideWebContainer() to able to use the provided boot promise.
const sandbox = useSandbox()
Mount File System Tree
Use onMounted()
to await the boot, then mount the fileTree
we created earlier.
onMounted(async () => {
// Ensure injected promise has been resolved
const container = await boot
// Continue initialisation
// Mount files
await container.mount(fileTree)
// Finish bootstrapping
await sandbox.bootstrap()
// Optionally, open one of the files for editing.
sandbox.editorTabs.open('./public/index.html')
})
Cleanup
Make sure to tear down the WebContainer once it is no longer needed.
onBeforeUnmount(() => sandbox.container.value?.teardown())
Template
Now simply add the KrgzSandbox
component to your template.
<template>
<!-- Hiding the solve button for now. Another story for another guide :) -->
<KrgzSandbox :booting="isBooting" hide-solve-button></KrgzSandbox>
</template>
Heads up!
In a Nuxt project <KrgzSandbox />
must be within a <ClientOnly />
block.
Bringing all of that together will get you this nice sandbox :)