How to Private‑Deploy and Customize CodeSandbox for a Cross‑Project Component Platform
This article explains the background, challenges, and step‑by‑step process of privately deploying and extending CodeSandbox—including building a custom online IDE, configuring a private npm registry, modifying the packager service, adding screenshot and style‑injection features, and integrating with Bit for component sharing.
Background
The author needed a cross‑frontend project component/segment sharing solution built on Bit . Bit synchronizes components across projects but lacks a public UI for browsing shared components, so a custom website with an online IDE was required.
Why Not Use Existing Free Online IDEs?
Company private npm registries cannot be accessed.
Specific build configurations (e.g., less files) are unsupported.
Custom functionality such as screenshot capture is missing.
Sensitive code cannot be exposed publicly.
Some build tools rely on node_modules, which browsers cannot provide.
Therefore a self‑hosted online IDE was needed. The author first tried a ground‑up implementation but switched to customizing an open‑source IDE due to effort and feature gaps.
CodeSandbox Basics
CodeSandbox was chosen because most of its functionality is open‑source. Its architecture consists of three parts:
Editor : edits code and notifies the sandbox.
Sandbox : runs code in an isolated iframe, handling transpilation and evaluation.
Packager : fetches npm package files for the sandbox.
The build pipeline has three stages:
Packager – downloads npm packages, builds a manifest of required files.
Transpilation – compiles code and creates a dependency graph (similar to webpack).
Evaluation – executes the compiled modules via eval inside the iframe.
Packager Implementation
The packager runs as an Express server, optionally deployed serverlessly on AWS Lambda. It downloads a package (e.g., [email protected]) with yarn, reads entry points from package.json, parses require statements, and builds a manifest.json that contains file contents and dependency metadata.
{
"contents": {
"/node_modules/react/index.js": {
"content": "'use strict'; ...",
"requires": ["./cjs/react.development.js"]
}
},
"dependencies": [{"name":"@babel/runtime","version":"7.3.1"}],
"dependencyAliases": {},
"dependencyDependencies": {"object-assign": {"entries":["object-assign"],"parents":["react","prop-types"]}}
}To speed up repeated builds, the manifest is cached in an S3 bucket; in a private deployment this can be replaced with any object storage.
Transpilation Details
When the sandbox receives source code, it downloads the manifest from the packager, then recursively parses and transpiles each module, building a dependency graph much like webpack. CodeSandbox supports several built‑in presets (e.g., create-react-app, vue-cli, parcel), each defining which transpilers to apply.
import babelTranspiler from "../../transpilers/babel";
const preset = new Preset(
"create-react-app",
["web.js","js","json","web.jsx","jsx","ts","tsx"],
{hasDotEnv:true, setup: manager => { /* custom options */ }}
);Private Deployment of CodeSandbox
Deploying the Packager Service
The open‑source dependency‑packager repository (Express‑based) can be cloned and deployed to a private server. If serverless is unavailable, comment out the Lambda‑specific code.
Configuring a Private npm Registry
Two approaches are shown:
Set the registry globally in a Dockerfile:
FROM node:12-alpine
COPY . /home/app
RUN cd /home/app && npm config set registry http://npm.xxx.com && npm install -f
WORKDIR /home/app
CMD ["npm","run","dev"]Pass --registry=http://npm.xxx.com to yarn in the packager’s install‑dependencies.ts script.
The packager’s code that builds the manifest URL is also adjusted to point to the private service.
const PROD_URLS = {
// replace with your own service
bucket: 'http://xxx.xxx.com'
};
function dependencyToPackagePath(name, version) {
return `${name}@${version}`;
}Building and Containerizing the Frontend
A multi‑stage Docker build compiles the CodeSandbox client, then copies the built www folder into an Nginx image.
FROM node:10.14.2 as build
WORKDIR /
ADD . .
RUN /bin/sh build.sh
FROM nginx:1.16.1-alpine
COPY --from=build /packages/app/www /usr/share/nginx/html/If building on the server is problematic, the author builds locally, pushes the artifact to a Git repository, and builds the image from that source.
Customizing CodeSandbox
Injecting Built‑in Component Styles
Because browser‑based builds lack node_modules, the author adds a step in the packager to read CSS files of built‑in components and embed them in the manifest:
const insertStyle = (contents, packageName, packagePath) => {
const stylePath = `node_modules/${packageName}/dist/index.css`;
const styleFilePath = join(packagePath, stylePath);
if (fs.existsSync(styleFilePath)) {
contents[stylePath] = { content: fs.readFileSync(styleFilePath, "utf-8"), isModule: false };
}
};The browser side then creates a <style> element and appends it to document.head during the evaluation phase.
const insertStyleNode = content => {
const styleNode = document.createElement('style');
styleNode.type = 'text/css';
styleNode.innerHTML = content;
document.head.appendChild(styleNode);
};Adding Screenshot Capability
A new message type SCREENSHOT_DATA is sent from the sandbox iframe after html2canvas captures the preview area. The parent page listens for this message and stores the image data.
const fetchScreenShot = async () => {
const app = document.querySelector('#root');
const c = await html2canvas(app);
const imgData = c.toDataURL('image/png');
window.parent.postMessage({type:'SCREENSHOT_DATA',payload:{imgData}}, '*');
};
window.addEventListener('message', event => {
const {type} = event.data;
if (type === 'FETCH_SCREENSHOT') fetchScreenShot();
}, false);Supporting LESS in create‑react‑app Preset
The preset file is extended to register lessTranspiler and a post‑CSS processor, enabling LESS compilation.
import lessTranspiler from '../../transpilers/less';
import styleProcessor from '../../transpilers/postcss';
preset.registerTranspiler(module => /\.less$/.test(module.path), [
{transpiler: lessTranspiler},
{transpiler: styleProcessor},
{transpiler: stylesTranspiler, options: {hmrEnabled:true}}
]);Redirecting Packager Requests
The URL of the npm packager service is swapped to the private endpoint, and the package path format is simplified.
const PROD_URLS = { bucket: 'http://xxx.xxx.com' };
function dependencyToPackagePath(name, version) {
return `${name}@${version}`;
}These four examples illustrate how to adapt CodeSandbox to corporate constraints such as private registries, custom component styling, screenshot capture, and additional build presets.
Conclusion
By privately deploying CodeSandbox and applying the customizations above, a fully functional online IDE can be built that respects internal npm registries, supports proprietary component styles, captures preview screenshots, and handles LESS files. The approach can be extended to server‑side builds for even richer development experiences.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
