NashTech Blog

WebContainers: A Revolutionizing Web Development

Table of Contents

It is a ground-breaking technology introduced by StackBlitz in 2021, set to revolutionize how developers build, test, and deploy web applications. By enabling full-stack Node.js environments to run entirely within the browser. In this article, we’ll explore what WebContainers is, its benefits and limitations, and how it transforms the development workflow. 

What is WebContainers?

WebContainers is a new type of web technology that allows you to run Node.js environments natively within your browser. Unlike traditional web development setups that require a local development environment, WebContainers leverage the power of WebAssembly to execute Node.js, enabling developers to spin up development environments instantly within the browser. 

StackBlitz, the pioneering company behind WebContainers, designed this technology to eliminate the need for complex local setups, making the development process more streamlined and accessible. By running the entire development stack in the browser, WebContainers provide a consistent and secure environment for coding, testing, and debugging applications. 

The Main Components?

Runtime Environment: WebContainers include runtime environments that support various programming languages used in web development, such as Node.js, Python, Ruby, PHP, and others. This allows developers to write code in their preferred language directly within the browser. 

Package Managers: WebContainers typically integrate with package managers like NPM (for Node.js), pip (for Python), gem (for Ruby), etc. These tools manage dependencies and libraries required by the application, ensuring that the necessary packages are installed and accessible. 

Virtual File System: Within the browser environment, WebContainers provide a virtual file system. This allows developers to create, edit, and manage project files, including source code, configuration files, assets (like images and stylesheets), and other resources needed for development. 

HTTP and Network Access: WebContainers enable applications to make HTTP requests and interact with external APIs and services over the internet. This networking capability is crucial for web applications that require data exchange with servers or external resources. 

Development Tools: WebContainers often include integrated development environments (IDEs) or IDE-like features. These may include code editors with syntax highlighting and auto-completion, debugging tools, terminal access for running commands, and version control integration (like Git). 

Security & Isolation: WebContainers employ sandboxing techniques. This isolates the application running within the container from the underlying browser environment and other containers, preventing unauthorized access and malicious actions. 

WebAssembly Execution: Some advanced WebContainers implementations support WebAssembly (Wasm). This allows applications written in languages like C, C++, Rust, or even compiled TypeScript to run efficiently within the browser, achieving near-native performance. 

Persistence and Collaboration Tools: WebContainers offer features for persisting application state across sessions. This can include local storage options or integration with cloud services for data storage and synchronization; For team-based development, WebContainers may include features for sharing development environments, facilitating collaboration, and enabling concurrent editing and code review. 

How It Works on Browser? 

WebContainers leverage several key components of web browsers to enable the execution of applications directly within the browser environment. Firstly, they utilize browser-based isolation techniques such as sandboxing, ensuring that applications operate securely without impacting the stability of other browser tabs or the underlying operating system.

Additionally, WebContainers integrate with WebAssembly (Wasm), a binary instruction format supported by modern browsers, allowing for efficient execution of compiled code at near-native speeds. They also make use of the browser’s virtualized file system capabilities, enabling applications to manage and access files securely within the browser environment.

Networking capabilities within WebContainers leverage the browser’s ability to make HTTP requests and handle network communications, facilitating interactions with external APIs and services. Moreover, they incorporate user interface elements and development tools directly within the browser interface, supporting coding, testing, and debugging workflows seamlessly. 

In short words, WebContainers harness the robust capabilities of web browsers to provide developers with a versatile, self-contained development environment directly accessible through a web browser.

The Limitations

WebContainers offer significant advantages for web application development but also come with certain limitations. One major constraint is performance, as executing applications within a browser environment may introduce overhead compared to native execution. This can impact the speed and responsiveness of complex applications, especially those requiring intensive computation or real-time processing.

Another limitation is compatibility and dependency management. WebContainers rely on browser support for technologies like WebAssembly and virtualized environments, which may vary across different browsers and versions. This can lead to inconsistencies in behavior and functionality, requiring developers to carefully consider compatibility issues when deploying applications. 

Security is also a concern, despite sandboxing measures. Ensuring robust isolation and protection against potential vulnerabilities in the browser environment remains a continual challenge.

Furthermore, resource constraints such as limited storage access and restricted network capabilities within browsers can restrict the types of applications that can effectively run in WebContainers.

Overall, while WebContainers offer flexibility and accessibility, developers must navigate these limitations to optimize performance, security, and compatibility for their applications. 

Why We Use It

WebContainers revolutionize web development by enabling near-native performance within the browser through WebAssembly, offering a seamless, high-speed execution of compiled code. They provide a virtualized environment that ensures isolation and security, allowing developers to run applications without worrying about system dependencies. This isolation simplifies scalability and enhances resource management, making it easier to maintain consistent performance. Additionally, WebContainers support persistent storage using browser APIs, ensuring data is saved and accessible across sessions. Their real-time collaboration capabilities enable multiple users to work simultaneously, fostering efficient teamwork. By eliminating the need for complex local setups and providing a consistent development environment, WebContainers streamline the development process, making it accessible from any device with a browser. This ease of use, combined with robust performance and collaborative features, makes WebContainers an essential tool for modern web development. 

For its limitations and key features provided. You may be considering for using WebContainers for the following use cases: 

Development and Testing: Quickly create and test full-stack applications without local setup. 

Education and Training: Provide students with consistent, ready-to-use environments. 

Prototyping and Demos: Share live prototypes and demos with stakeholders instantly. 

Build a WebContainers Application

WebContainers application is a full-stack application, including 2 applications, backend and frontend that leverages modern JavaScript and TypeScript tools with mono-repo management. The backend application provides the features related to server and systems tasks. In this demo, it is used to clone another application from GitHub and then prepare some data for frontend to mount to browser container. Besides that, frontend application will provide some features that will work with backend, command lines and browser container to receive and render data.

Below are some main steps:

Step 1: Using Turborepo to initialize a project with backend using Express and frontend using Vite.

Step 2: In apps/backend

Install simple-git package that allows to clone repo from GitHub

>> pnpm add simple-git

Create a function to clone a code repo from GitHub

const fs = require("fs").promises;
const simpleGit = require("simple-git");

async function clonePublicRepo(directoryPath, repoUrl) {
  const git = simpleGit();
  try {
    await fs.access(directoryPath);
    await fs.rm(directoryPath, { recursive: true, force: true });
  } catch (error) {
    await fs.mkdir(directoryPath, { recursive: true });
  }
  await git.clone(repoUrl, directoryPath);
}

module.exports = { clonePublicRepo };

Create a function to refine and prepare data for mounting

async function constructFilesObject(directoryPath, repoUrl) {
  const entries = await fs.readdir(directoryPath, { withFileTypes: true });
  const files = {};
  for (const entry of entries) {
    const fullPath = path.join(directoryPath, entry.name);
    if (entry.isDirectory()) {
      files[entry.name] = {
        directory: await constructFilesObject(fullPath, repoUrl),
      };
    } else if (!entry.name.startsWith(".git")) {
      const contents = await fs.readFile(fullPath, "utf8");
      files[entry.name] = {
        file: {
          contents,
        },
      };
    }
  }
  return files;
}

module.exports = { constructFilesObject };

Create an endpoint that return data for mounting

app.get("/mounted-data", async (req, res) => {
  try {
    const repoUrl = process.env.GIT_REPO;
    const mountPath = process.env.MOUNT_PATH;
    await clonePublicRepo(mountPath, repoUrl);
    const files = await constructFilesObject(mountPath, repoUrl);
    return res.json(files);
  } catch (error) {
    console.error("Failed to construct files object:", error);
    res.status(500).send("Internal Server Error");
  }
});

Step 3: In apps/frontend

Install some following packages:

>> pnpm add @webcontainer/api>> pnpm add @xterm/addon-fit
>> pnpm add @xterm/xterm

Add the following code into main.js to bootstrap the shell and render to result to frontend

import "@xterm/xterm/css/xterm.css";
import "./style.css";
import { initApp } from "./libs/shell";

window.addEventListener("load", initApp());

document.querySelector(".terminal").innerHTML = `
  <div class="terminal-be"></div>
  <div class="terminal-fe"></div>
`;

Implement the initApp() function

/** Main application initialization */
export async function initApp() {
  const response = await axios.get("http://localhost:8080/mounted-data");
  const files = response.data;

  const textareaEl = document.querySelector("textarea");
  textareaEl.value = files["package.json"].file.contents;
  textareaEl.addEventListener("input", (e) => {
    writeIndexJS(e.currentTarget.value);
  });

  const fitAddon = new FitAddon();
  const beTerminalEl = document.querySelector(".terminal-be");
  const feTerminalEl = document.querySelector(".terminal-fe");

  const terminalBe = initTerminal(beTerminalEl, fitAddon);
  const terminalFe = initTerminal(feTerminalEl, fitAddon);
  fitAddon.fit();

  webcontainerInstance = await WebContainer.boot();
  await webcontainerInstance.mount(files);

  webcontainerInstance.on("server-ready", (port, url) => {
    document.querySelector("iframe").src = url;
  });

  const shellProcessBe = await startShell(terminalBe, true);
  setupWindowResizeListener(terminalBe, shellProcessBe);

  const shellProcessFe = await startShell(terminalFe);
  setupWindowResizeListener(terminalFe, shellProcessFe);
}

The apps after deploying will look like following:

Full source code: https://github.com/cuongle22/webcontainers-app 

Picture of cuonglecao

cuonglecao

A Senior Software Engineer at NashTech.

Leave a Comment

Your email address will not be published. Required fields are marked *

Suggested Article

Scroll to Top