惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
C
Cisco Blogs
P
Privacy & Cybersecurity Law Blog
C
CERT Recently Published Vulnerability Notes
P
Palo Alto Networks Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
T
Threat Research - Cisco Blogs
Schneier on Security
Schneier on Security
S
Securelist
N
News | PayPal Newsroom
I
Intezer
H
Hacker News: Front Page
P
Privacy International News Feed
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
A
About on SuperTechFans
P
Proofpoint News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Project Zero
Project Zero
Cloudbric
Cloudbric
N
News and Events Feed by Topic
大猫的无限游戏
大猫的无限游戏
Attack and Defense Labs
Attack and Defense Labs
博客园 - 叶小钗
S
Security @ Cisco Blogs
L
LINUX DO - 最新话题
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
博客园 - Franky
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
GbyAI
GbyAI
Google DeepMind News
Google DeepMind News
I
InfoQ
有赞技术团队
有赞技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
S
Security Affairs
Apple Machine Learning Research
Apple Machine Learning Research
腾讯CDC
Security Archives - TechRepublic
Security Archives - TechRepublic
V
Vulnerabilities – Threatpost
T
The Blog of Author Tim Ferriss
Scott Helme
Scott Helme

Vercel News

Vercel Open Source Program: Winter 2026 cohort How Notion Workers run untrusted code at scale with Vercel Sandbox How we run Vercel's CDN in front of Discourse From idea to secure checkout in minutes with Stripe Building Slack agents can be easy Scaling redirects to infinity on Vercel Advancing Python typing Gamma builds design-first agents with Vercel How Avalara turns pipe dreams into patent-pending with v0 Keeping community human while scaling with agents How OpenEvidence built a healthcare AI that physicians actually trust Security boundaries in agentic architectures Skills Night: 69,000+ ways agents are getting smarter Video Generation with AI Gateway We Ralph Wiggumed WebStreams to make them 10x faster How Stably ships AI testing agents in hours, not weeks How we built AEO tracking for coding agents Anyone can build agents, but it takes a platform to run them Introducing Geist Pixel The Vercel AI Accelerator is back with $6m in credits Making agent-friendly pages with content negotiation The Vercel OSS Bug Bounty program is now available Introducing the new v0 Run untrusted code with Vercel Sandbox, now generally available How Stripe built a game-changing app in a single flight with v0 How Sensay went from zero to product in six weeks AGENTS.md outperforms skills in our agent evals Agent skills explained: An FAQ Testing if "bash is all you need" AWS databases are now live on the Vercel Marketplace and v0 Use Perplexity Web Search with Vercel AI Gateway Introducing: React Best Practices Nick Bogaty joins Vercel as Chief Revenue Officer How Mux shipped durable video workflows with their @mux/ai SDK How to build agents with filesystems and bash How we made v0 an effective coding agent Stopping the slow death of internal tools Building AI-Generated Pixel Trading Cards with Vercel AI Gateway We removed 80% of our agent’s tools AI SDK 6 Our $1 million hacker challenge for React2Shell Cline now runs on Vercel AI Gateway How to prompt v0 Build smarter workflows with Notion and v0 Vercel launches partner certification Inside Workflow DevKit: How framework integrations work React2Shell Security Bulletin | Vercel Knowledge Base Billions of requests: Black Friday-Cyber Monday 2025 Investing in the Python ecosystem AWS Databases coming to the Vercel Marketplace How we built the v0 iOS app Workflow Builder: Build your own workflow automation platform Vercel Open Source Program: Fall 2025 cohort Self-driving infrastructure Vercel collaborates with Google for Gemini 3 Pro Preview launch Vercel: The anti-vendor-lock-in cloud How Nous Research used BotID to block automated abuse at scale How AI Gateway runs on Fluid compute What we learned building agents at Vercel Build and deploy data applications on Snowflake with v0 BotID Deep Analysis catches a sophisticated bot network in real-time Vercel achieves TISAX AL2 compliance to serve automotive partners Bun runtime on Vercel Functions David Totten Joins Vercel to Lead Global Field Engineering Vercel Ship AI 2025 recap You can just ship agents AI agents and services on the Vercel Marketplace Built-in durability: Introducing Workflow Development Kit Zero-config backends on Vercel AI Cloud Introducing Vercel Agent: Your new Vercel teammate Update regarding Vercel service disruption on October 20, 2025 Agents at work, a partnership with Salesforce and Slack Running Next.js in ChatGPT: How to Build ChatGPT Apps Talha Tariq joins Vercel as CTO of Security Just another (Black) Friday Server rendering benchmarks: Fluid Compute and Cloudflare Workers Towards the AI Cloud: Our Series F Collaborating with Anthropic on Claude Sonnet 4.5 to power intelligent coding agents Preventing the stampede: Request collapsing in the Vercel CDN BotID uncovers hidden SEO poisoning How we made global routing faster with Bloom filters What you need to know about vibe coding Scale to one: How Fluid solves cold starts Addressing security & quality issues with MCP tools - Vercel AI agents at scale: Rox’s Vercel-powered revenue operating system Agentic Infrastructure Zero Data Retention on AI Gateway Optimizing Vercel Sandbox snapshots How Waldium made a blog platform work for humans and AI alike How FLORA shipped a creative agent on Vercel's AI stack Agent responsibly Making Turborepo 96% faster with agents, sandboxes, and humans Unified reporting for all AI Gateway usage new.website joins forces with v0 SERHANT.'s playbook for rapid AI iteration Two startups at global scale without DevOps Chat SDK brings agents to your users 360 billion tokens, 3 million customers, 6 engineers Meet the 2026 Vercel AI Accelerator Cohort Build knowledge agents without embeddings
Build your own web framework - Vercel – Vercel
Lydia Hallie · 2022-07-28 · via Vercel News

11 min read

Deploy any framework to Vercel using the Build Output API.

Have you ever wondered what it takes to build your own web framework that also deploys to edge and serverless infrastructure? What features does a modern framework need to support, and how can we ensure that these features allow us to build a scalable, performant web application?

This post will explain how to build your own simple React-based web framework. We'll use the Vercel Build Output API to deploy our framework with support for the following features:

  • Static Files to statically render pages

  • Incremental Static Regeneration to automatically revalidate and regenerate pages after a specific timeout

  • Edge Functions to enable edge rendering and middleware

  • Automatic Image Optimization to efficiently serve the images using the latest format, enable lazy loading, and prevent layout shift

  • Serverless Functions to server-render dynamic pages and create data fetching endpoints

  • Edge Caching to quickly serve static files to users globally

The source code for this article is available in this GitHub Repo to see the demo implementation covered in this article.

The implementation of this demo framework is simplified. Production frameworks usually do more to create an optimized output, such as more advanced bundling, caching, type checking, and more. This blog post demonstrates how to create a basic yet functional example and deploy it to Vercel using the Build Output API.

Link to headingLanding Page

Let's explore the requirements of each page we want to build and how to build optimizations into our web framework to help achieve excellent performance.

Demo website's landing pageDemo website's landing page

Demo website's landing page

The landing page is a static page with a single large hero image. Although this page may seem simple, there are a few optimizations we can implement in our framework to ensure this page is fast.

Image Optimization


We can reduce the image size without sacrificing quality by using the latest image formats (like .webp and .avif) and prevent layout shift by explicitly setting the image width and height.

Edge Caching


Since we want our framework to generate a static HTML file for this page, we can use a CDN to improve the Time To First Byte (TTFB) by caching the file at each region (or edge). Vercel provides this functionality out of the box with the Edge Network, so we don't need to add this ourselves.

Link to headingProducts Page

Demo website's product pageDemo website's product page

Demo website's product page

The products page is a hybrid between a pre-generated static HTML page and a dynamic server-rendered page. The page renders a list of products retrieved from a data provider and should automatically update after a certain amount of time (or when new products have been added).

This product page can benefit from a few optimizations we want to build into our framework. 

Image Optimization

Since we're showing many images on this page, we want to use image optimization to serve the images in the latest format and defer the loading of non-critical images for a faster first paint.

Incremental Static Regeneration

Displayed products should update after a certain amount of time or when new products are added. This rendering pattern is also referred to as Incremental Static Regeneration behavior and gives you the benefits of Static Generation, combined with the dynamic benefits of Server-Side Rendering. When using the Build Output API, we can use Prerender Functions to enable and configure ISR on certain pages.

Link to headingPopular Page

Demo website's popular pageDemo website's popular page

Demo website's popular page

We want to display the most relevant, personalized products to our visitors. How can our web framework help enable us to add this feature?

Edge Functions

To get the visitor’s location, we can use an Edge Function to determine the city based on the x-vercel-ip-city header. We could take two approaches to Edge Functions: either use Edge Middleware to redirect the user to a specific page based on their location, or we can server-render the page using Edge Functions. We’ll choose server-rendering for a fast, personalized page in this case.

Link to headingBuilding the framework

We now know what features our website could benefit from to create a better user experience. Next, let’s focus on implementing our framework to ensure it supports all the features mentioned above.

Our framework expects the pages to be located in the pages/ folder. To keep it simple, we’ll expect that the page contains:

  • A default export to export the page’s React component

  • A configuration object named pageConfig, which includes a strategy prop to define the rendering technique that should be used for this page - either static, ssr, prerender, or edge . If a page is prerendered, users can also pass an optional  expiration time and fallback component to configure the regeneration

Let's see how our framework can work with these values and create a valid output Vercel can use to deploy our project.

Link to headingStatic Rendering

When a page is statically rendered, the page’s HTML is generated during the build. Vercel's Edge Network can quickly return this pre-generated HTML file when a user visits the page, after which the browser can draw the contents to the user’s screen.

To support static pages, we first have to implement a transpilation step that turns React-based pages into static HTML. ReactDOM Server exposes a method called renderToString, which takes a React component and returns the corresponding HTML output. We can invoke this function to prerender the HTML for static pages during the build.

function createStaticPage(pagePath) {

const { Component } = require(pagePath);

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

...

}

In most cases, however, our React components aren’t entirely static. Components usually contain some interactivity, such as event handlers. To account for this, we also need to create one or multiple JavaScript bundle(s) to hydrate the static markup once it’s been rendered.

To hydrate dynamic components, we can export a string literal that our bundler eventually uses to create a custom hydration script for the individual pages. A bundler can generate HTML and automatically inject this custom hydration script as a deferred script. The script automatically fetches the hydration bundle to add interactivity when a browser has loaded the page's HTML.

Let’s see what this could look like in our code.

The example below shows a dynamic createStaticFile method that takes our page’s default exported component and the filePath where the component is located. With this information, we can create a bundle that automatically gets injected into the newly created HTML file and output it to the .vercel/output/static folder.

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>`

);

}

At build time, we end up with a .vercel/output/static folder that contains the static HTML and the JavaScript bundle(s) necessary for hydration.

Any static assets, such as HTML, CSS, JavaScript, and images should be located in the .vercel/output/static folder so the Vercel Build Output API can convert them into infrastructure primitives.

Link to headingIncremental Static Regeneration

Incremental Static Regeneration is a powerful pattern that we can use for pages that contain data that frequently updates by invalidating the cache and regenerating the page after a specific interval or based on an event.

The /products page can benefit from ISR, as it could regenerate after a specific interval to ensure it always shows the latest products and optional price reductions as quickly as possible.

To enable Incremental Static Regeneration, we need to create a few files.

  • products.func/index.js: A Serverless Function containing the handler that takes care of the page’s (re)generation.

  • products.func/vc-config.js: The configuration file used by the Serverless Function to configure its environment, such as the runtime and optional helpers.

  • products.prerender-config.json: The configuration file that Vercel uses to determine when and how to regenerate the page. This is necessary since this isn’t just Server-Rendering—it’s also using automatic invalidation and regeneration.

  • products.prerender-fallback.html: The (optional) fallback HTML that gets served when there’s no cached version of the page available yet and the page is being generated in the background. This creates a better user experience since users don’t have to stare at a blank screen while the page is generated.

Link to heading
Serverless Functions

First, let’s see how we can create a Serverless Function that handles the HTML (re)generation.

When a Serverless Function is invoked, its handler function runs. This function runs in its own environment, meaning that we have to ensure that this handler function has access to all the necessary code to generate the HTML.

We can create a Serverless Function on Vercel by creating a <name>.func folder, with the name being the name of the server-rendered page. In this case, we have to create a products.func folder, since the page needs to get regenerated in a Serverless Function.

To include all the necessary code and dependencies, we can create a node_modules folder within the function folder or bundle everything together into one single handler file.

To create the files that are necessary for a serverless function, let’s create a createServerlessFunction function that invokes two functions:

  • generateClientBundle, to generate a client-side bundle used to hydrate the static HTML returned from the server

  • generateLambdaBundle, to bundle the necessary files into a script that’s executed in the Lambda’s handler

The method also generates a .vc-config.json file, which includes information that the Lambda needs to set up its execution context.

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,

});

}

The handler function is responsible for server-rendering the HTML based on the page’s exported component. This approach is very similar to the static rendering we saw before. This time, however, we’re generating the HTML at request time instead of at build time.

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>\`)

}

`;

After bundling, the .vercel/output/functions/products.func/index.js handler file includes the functionality to server-render the component’s HTML.

Besides creating a Serverless Function, we also need a prerender-config.json file that contains information about the regeneration when we’re using Incremental Static Regeneration. Our framework allows the users to set their own revalidate time, so we can dynamically create this config file.

Our new createPrerender function calls the createServerlessFunction and createStaticFile functions that we created before, and creates a <name>.prerender-config.json. We’re creating a static file to render this as a fallback page.  This page gets shown to the user when the page is still being generated in the background.  The Build Output API expects this fallback HTML to be located at <name>.prerender-fallback.html , or as specified in the <name>.prerender-config.json.

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`,

}

);

}

Once building has been completed, we end up with a products.func folder containing all the code it needs to generate the HTML on the server.

Right now, the createPrerender function takes care of creating the Serverless Function, creating a static prerender-fallback.html fallback file by statically rendering the component at build time, and generating a prerender-config.json configuration file that includes the necessary information the Build Output API needs to configure the prerendering behavior, such as the expiration value.

Link to headingEdge Server-Rendering

Since React is compatible with the Edge Runtime due to its isomorphic nature - it doesn’t use any Node.js libraries - it’s possible to render React at the Edge. Creating an Edge Function is similar to a serverless function, with its runtime as the most significant difference.

Let’s create a createEdgeFunction function that takes care of generating the necessary files to enable Edge Server-Rendering. This function calls the generateEdgeBundle function that eventually takes care of bundling the required files, and creates a .vc-config.json configuration file that indicates we’re using the edge runtime with an entrypoint file instead of a handler.

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",

});

}

Generating the bundle is similar to the serverless approach, however this time, we care about values present on the req object. To pass the prop down to the edge-rendered page, we can dynamically create the React element that was bundled from the original page’s file path.

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' }

});

}

`;

After bundling, we now end up with an entrypoint that passes the req prop to the edge-rendered page and returns a new Response object with the generated HTML.

Link to headingServerless Functions

Serverless Functions can be used to server-render pages or to create data fetching endpoints.

When we implemented Incremental Static Regeneration in the previous paragraph, we created a Serverless Function responsible for generating the page’s HTML. Server-Side Rendering uses the same approach - with the main differences being when the Serverless Function gets invoked and its caching behavior.

To server-render a page, we need to create a function that generates the page’s HTML on every request.

You can see that it’s essentially a subset of the Incremental Static Regeneration code. In this case, we’re only calling the createServerlessFunction method covered in the previous paragraph to create both a lambda (serverless) bundle and a client-side bundle.

With ISR, this lambda only gets invoked when a user requests a page that had been cached longer than the revalidate value. Vercel then automatically regenerates the page.

With server-side rendering, however, this function is invoked on every request. Vercel won’t automatically cache responses from this function when the page is server-rendered, resulting in unique responses every time.

Now that we’re supporting the rendering techniques, let’s see how to implement Image Optimization.

Link to headingAutomatic Image Optimization

Vercel can automatically optimize images by pointing the image src to /_vercel/image?url=  and adding the necessary configuration. For our framework, we'll add support for a vercel.config.js file, where the Image Optimization configuration can be defined.

Let’s make it easy for our users to use optimized images by exporting an Image component. This component ensures that the src point to the /_vercel/image path and adds the necessary height and width to serve the correct image size based on the viewport.

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`}

/>

)

}

You can now import the Image component and use it like a regular img tag. The only part required is some configuration in the vercel.config.js file, such as the domain if it’s an external domain, the image size, and the modern image format we want to use. This file is used by our framework and outputs its contents to .vercel/output/config.json.

vercel.config.json

export default {

images: {

domains: [...],

sizes: [...],

minimumCacheTTL: 60,

formats: [

"image/webp",

"image/avif"

]

}

}

After adding this configuration, any image that uses our custom Image component can benefit from the Automatic Image Optimization feature Vercel provides.

Link to headingConclusion

Now that we have the necessary methods to support the rendering patterns and optimize images, we can traverse the pages directory and invoke the functions to create the required files. We’re also copying all the static files - such as images, CSS, and JavaScript - from the project’s public folder to the .vercel/output/static folder and creating a .vercel/output/config.json file based on the project’s vercel.config.js.

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,

},

});

...

}

When invoking this method, we can create a valid .vercel/output folder that allows us to deploy to Vercel, using some of the cutting-edge features that the platform provides.

When using modern frameworks such as Next.js, we luckily don’t have to worry about implementing all these steps since the optimizations are provided out of the box.

However, if you’re an independent developer that wants to benefit from the platform’s features or a framework author that’s looking to integrate with Vercel, the Build Output API makes it easy to build any project on Vercel.

Although it should be clear that this framework should not be used in production—it just does the bare minimum and can be optimized—it’s good to see how modern frameworks make development so much easier.

The code is available in this GitHub Repo to see the demo implementation covered in this article. If you’d like another example of using the Build Output API, check out Astro, a framework that has successfully integrated with Vercel.