Search⌘ K
AI Features

Progressive Rendering

Explore how progressive rendering enhances web performance by prioritizing and loading visible page components first. Understand techniques like lazy loading images to reduce wait times and improve user experience compared to full server-side rendering.

Progressive rendering

You might have noticed that most modern websites render gradually. They don’t appear all at once, but in parts. This is because the page rendering process is not done all in one go. The browser starts receiving, parsing, and rendering whatever HTML it can, even if it has not received all of it.

This is done to make waiting a little easier for the user. Try counting how long it takes for a website to fully render and imagine if you had to wait that long to see anything on the screen!

This method is called progressive rendering, and is sort of in-between server-side and client-side rendering. The components of the page are rendered on the server and sent in order of priority. So, the highest priority components are rendered on the server, sent to the browser, and painted on the browser first. This gives the effect that you see on most modern websites, where parts of it are loaded and displayed first.

One way that content can be prioritized is by prioritizing what’s visible. One popular method to do this for images is called ‘lazy loading.’ Here, an image is only loaded onto the page when the user scrolls to it and it becomes visible. There are many ways to do this. One way is to use the HTML lazy attribute.

<img src="myimage.jpg" loading="lazy" alt="..." />

Scroll to the bottom of the page! We’ve lazy-loaded an image there.

Example

The best way to learn is by example. Run the following app, and go to the following URLs to see the differences between a progressively rendered page and one rendered entirely on the server-side.

#!/usr/bin/env node

/**
 * Module dependencies.
 */

const app = require('../app');
const debug = require('debug')('express-stream-html:server');
const http = require('http');

/**
 * Get port from environment and store in Express.
 */

const port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
 * Create HTTP server.
 */

const server = http.createServer(app);

/**
 * Listen on provided port, on all network interfaces.
 */

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
 * Normalize a port into a number, string, or false.
 */

function normalizePort(val) {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    // named pipe
    return val;
  }

  if (port >= 0) {
    // port number
    return port;
  }

  return false;
}

/**
 * Event listener for HTTP server "error" event.
 */

function onError(error) {
  if (error.syscall !== 'listen') {
    throw error;
  }

  const bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;

  // handle specific listen errors with friendly messages
  switch (error.code) {
    case 'EACCES':
      console.error(bind + ' requires elevated privileges');
      process.exit(1);
      break;
    case 'EADDRINUSE':
      console.error(bind + ' is already in use');
      process.exit(1);
      break;
    default:
      throw error;
  }
}

/**
 * Event listener for HTTP server "listening" event.
 */

function onListening() {
  const addr = server.address();
  const bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
  console.log('Listening on ' + 'http://localhost:' + port);
}

Progressive rendering

https://yourAppURL.educative.run/newspaper/streamhttps://yourAppURL.educative.run/newspaper/stream

Server-side rendering

https://yourAppURL.educative.run/newspaperhttps://yourAppURL.educative.run/newspaper

Do you notice how much better the user experience for progressive rendering is when compared to the server-side example?

Lazy-loading example

Check out this image that we’ve lazy-loaded! You might notice that it doesn’t immediately pop up if your Internet connection isn’t insanely fast.