Skip to content
Dashboard

Build your own web framework

Staff Developer Advocate

Deploy any framework to Vercel using the Build Output API.

Link to headingLanding Page

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

Link to headingProducts Page

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

Link to headingPopular Page

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

Link to headingBuilding the framework

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

Link to headingIncremental Static Regeneration

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

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' }
});
}
`;

Link to headingVercel Functions

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"
]
}
}

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?