The Progressive Rendering Format is an open specification that enables edge-accelerated streaming and partial rendering for any web framework. It allows you to deliver static content instantly from the edge while progressively loading dynamic content.
For an overview and background on this format, see our blog post introducing the Progressive Rendering Format.
The Progressive Rendering Format uses a structured JSON response format to define:
When a request is processed through Tilda's infrastructure, our edge network:
progressive-rendering-format: 1
headerThe Progressive Rendering Format is defined as a JSON structure with the following schema:
{
"v1": {
"status": 200, // HTTP status code
"headers": { // Headers for the final response
"content-type": ["text/html; charset=utf-8"]
},
"body": [ // Array of body segments
{
"text": "<!DOCTYPE html>..." // Static content (delivered immediately)
},
{
"remoteBody": { // Dynamic content (fetched and streamed)
"relativeUrl": "/api/data",
"method": "GET",
"forwardRequestHeaders": true,
"additionalHeaders": {}
}
},
{
"text": "</div></body></html>" // More static content
}
]
}
}
Body segments can be one of two types:
text
property):remoteBody
property):The remoteBody
object supports the following properties:
{
"remoteBody": {
"relativeUrl": "/api/data", // URL to fetch content from
"method": "GET", // HTTP method (GET, POST, etc.)
"forwardRequestHeaders": true, // Forward original request headers
"additionalHeaders": { // Additional headers to include
"custom-header": "value"
},
"body": "request body content" // Optional body for POST/PUT requests
}
}
The format supports special variables that dynamically insert values from the original request:
{
"remoteBody": {
"relativeUrl": "$$requestPath$$", // Original request path
"method": "$$requestMethod$$", // Original request method
"additionalHeaders": {
"x-original-url": "$$requestUrl$$", // Full original URL
"x-original-host": "$$requestHost$$" // Original host
}
}
}
Variable | Description |
---|---|
$$requestPath$$ | The path portion of the original request URL |
$$requestMethod$$ | The HTTP method of the original request |
$$requestUrl$$ | The full URL of the original request |
$$requestQuery$$ | The query string of the original request (without the ? ) |
$$requestHost$$ | The host of the original request |
$$requestRelativeUrl$$ | The path and query of the original request |
Variable | Description |
---|---|
$$requestPathPrefixedWith | Original path prefixed with /_next/postponed/resume |
$$requestRelativeUrlPrefixedWith | Original path and query prefixed with /_next/postponed/resume |
To use the Progressive Rendering Format in your application:
The easiest way to get started is with Next.js PPR, which is already integrated with the Progressive Rendering Format:
tilda build nextjs --ppr=15-canary-v1
See the Next.js PPR documentation for more details.
For maximum performance, you can pre-generate Progressive Rendering Format JSON files at build time and configure your routing to serve them with the appropriate headers:
.tilda/stage
or your own build directory)progressive-rendering-format: 1
headerThis approach is used by the Tilda CLI's tilda build nextjs --ppr
command to implement Next.js PPR. It:
.tilda/stage
directoryprogressive-rendering-format: 1
headerThis same approach can be used for any framework by creating a custom build process that:
// 1. Generate Progressive Rendering Format files during build
const progressiveRenderingFile = {
v1: {
status: 200,
headers: {
'content-type': ['text/html; charset=utf-8']
},
body: [
{ text: '<html><head>...</head><body><div id="app">' },
{
remoteBody: {
relativeUrl: '/api/dynamic-data',
method: 'GET',
forwardRequestHeaders: true
}
},
{ text: '</div></body></html>' }
]
}
};
// 2. Write to files in your build directory
fs.writeFileSync(
'.tilda/stage/route-name.progressiverendering.json',
JSON.stringify(progressiveRenderingFile)
);
// 3. Configure routing (e.g., in Tilda routing config)
const routingConfig = {
routes: [{
criteria: { path: { exact: '/route-path' }, method: ['GET', 'HEAD'] },
action: {
origin: 'static',
headers: [['progressive-rendering-format', '1'], ['content-type', 'application/json']],
staticFileRelativePath: 'route-name.progressiverendering.json'
}
}]
};
// 4. Use the Tilda CLI to deploy with your routing config and preserved stage directory
// tilda build --routingConfigJson '...' --preserveStage
Custom implementations can generate these files in the .tilda/stage
directory or their own build directory, and then use the tilda build
command with appropriate routing configuration and the --preserveStage
flag if needed.
For applications that need to generate the Progressive Rendering Format dynamically at request time, your server needs to:
progressive-rendering-format: 1
Tilda-Cache-Control
header with a large max-age
value (e.g., max-age=31536000
) to ensure the JSON format response is cached on the CDNImportant: The Tilda-Cache-Control
header mentioned above applies to the HTTP response containing the JSON Progressive Rendering Format, not the inner response headers defined within the format itself. Proper caching of the format response is crucial for performance as it allows the static shell to be served directly from edge nodes.
Example server-side implementation (pseudo-code):
async function handleRequest(request) {
// Determine which parts should be static vs. dynamic
const staticShell = generateStaticShell();
const dynamicContentUrl = '/api/dynamic-content';
// Return Progressive Rendering Format response
return new Response(JSON.stringify({
v1: {
status: 200,
headers: {
'content-type': ['text/html; charset=utf-8']
},
body: [
{ text: staticShell.start },
{
remoteBody: {
relativeUrl: dynamicContentUrl,
method: 'GET',
forwardRequestHeaders: true
}
},
{ text: staticShell.end }
]
}
}), {
headers: {
'content-type': 'application/json',
'progressive-rendering-format': '1',
// This header applies to the Progressive Rendering Format response itself
// This ensures the format definition is cached at the edge
'tilda-cache-control': 'public, max-age=0, max-age=31536000'
}
});
}
Implementing the Progressive Rendering Format provides several key advantages:
Be aware of the following current limitations: