Skip to content
Dashboard

Build your own web framework

Staff Developer Advocate

Deploy any framework to Vercel using the Build Output API.

Copy link to headingLanding Page

Demo website's landing pageDemo website's landing page
Demo website's landing page

Copy link to headingProducts Page

Demo website's product pageDemo website's product page
Demo website's product page

Copy link to headingPopular Page

Demo website's popular pageDemo website's popular page
Demo website's popular page

Copy link to headingBuilding the framework

Copy link to headingStatic Rendering

function createStaticPage(pagePath) {
const { Component } = require(pagePath);
const pageHTML = `<div id="root">${ReactDOMServer.renderToString(Component)</div>`;
...
}

export async function createStaticFile(Component,filePath) {
const pageName = getPageName(filePath);
const outdir = join(".vercel", "output", "static");
await fs.ensureDir(outdir);
await generateClientBundle({ filePath, outdir, pageName });
return fs.writeFileSync(
path.join(outdir, `${pageName}.html`),
`<!DOCTYPE html>
...
<body>
<div id="root">${ReactDOMServer.renderToString(React.createElement(Component) )}</div>
<script src="${pageName}.bundle.js" defer></script>
</body>`
);
}

Folder structure for static assetsFolder structure for static assets
Folder structure for static assets

Copy link to headingIncremental Static Regeneration

Copy link to heading**

export async function createServerlessFunction(Component, filePath) {
const pageName = getPageName(filePath);
const funcFolder = `.vercel/output/functions/${pageName}.func`;
await fs.ensureDir(funcFolder);
await Promise.allSettled([
generateClientBundle({ filePath, pageName }),
generateLambdaBundle({
funcFolder,
pageName,
Component,
}),
]);
return fs.writeJson(`${funcFolder}/.vc-config.json`, {
runtime: "nodejs16.x",
handler: "index.js",
launcherType: "Nodejs",
shouldAddHelpers: true,
});
}

export async function generateLambdaBundle(Component, funcFolder, pageName,outfile) {
const html = ReactDOMServer.renderToString(React.createElement(Component));
const { code: contents } = await transform(getHandlerCode(html, pageName));
return await build({
...
stdin: { contents, resolveDir: path.join(".") },
outfile,
});
};
const getHandlerCode = (html: string, pageName: string) => `
export default (req, res) => {
res.setHeader('Content-type', 'text/html');
res.end(\`<!DOCTYPE html>
<html lang="en">
...
<body>
<div id="root">${html}</div>
<script src="${pageName}.bundle.js" defer></script>
</body>
</html>\`)
}
`;

export async function createPrerender(Component, filePath, pageConfig) {
const pageName = getPageName(filePath);
const funcFolder = `.vercel/output/functions/${pageName}.func`;
await fs.ensureDir(funcFolder);
await Promise.allSettled([
createServerlessFunction(Component, filePath),
createStaticFile(Component, filePath, {
outdir: `.vercel/output/functions`,
fileName: `${pageName}.prerender-fallback.html`,
bundle: false,
}),
]);
return writeJson(
`.vercel/output/functions/${pageName}.prerender-config.json`,
{
expiration: pageConfig.revalidate,
group: 1,
fallback: `${pageName}.prerender-fallback.html`,
}
);
}

Folder structure for prerender functionsFolder structure for prerender functions
Folder structure for prerender functions

Copy link to headingDynamic server-rendering

export async function createEdgeFunction(Component, filePath) {
const pageName = getPageName(filePath);
const funcFolder = `.vercel/output/functions/${pageName}.func`;
await ensureDir(funcFolder);
await generateEdgeBundle({
funcFolder,
filePath,
pageName,
Component,
});
return writeJson(`${funcFolder}/.vc-config.json`, {
runtime: "edge",
entrypoint: "index.js",
});
}

export async function generateEdgeBundle(funcFolder, pageName, filePath) {
const { code: contents } = await transform(
getEdgeHandlerCode(filePath),
edgeBuildConfig
);
return await build({
...
stdin: { contents, resolveDir: path.join(".") },
outfile,
});
}
export const getEdgeHandlerCode = (filePath) => `
import { createElement } from 'react';
import { renderToString } from 'react-dom/server';
import Component from '${filePath}';
export default async function(req) {
const html = renderToString(createElement(Component, { req }));
return new Response(\`<!DOCTYPE html><div id="root">${html}</div>\`, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
`;

Copy link to headingVercel Functions

Copy link to headingAutomatic Image Optimization

export const Image = (props) => {
return (
<img
{...props}
ref={ref}
width={props.width}
height={props.height}
src={`/_vercel/image?url=${encodeURIComponent(props.src)}&w=${props.width}&q=75`}
/>
)
}

vercel.config.json
export default {
images: {
domains: [...],
sizes: [...],
minimumCacheTTL: 60,
formats: [
"image/webp",
"image/avif"
]
}
}

Copy link to headingConclusion

async function buildVercelOutput() {
...
await Promise.allSettled(
getRoutes().map(async (filePath) => {
const { pageConfig, default: Component } = await import(filePath);
switch (pageConfig.strategy) {
case "static":
return createStaticFile(Component, filePath);
case "prerender":
return createPrerender(Component, filePath, pageConfig);
case "ssr":
return createServerlessFunction(Component, filePath);
case "edge":
return createEdgeFunction(Component, filePath);
default:
return;
}
})
);
await copy("public", ".vercel", "output", "static")
return writeJSON(".vercel/output/config.json", {
...(require(process.cwd() + "/vercel.config.js").default),
...{
version: 3,
routes: getTransformedRoutes({
cleanUrls: true
}).routes,
},
});
...
}

Ready to deploy?