In this article, we walk through building out a full-stack real-time and completely serverless application that allows you to create polls! All of the app’s static bits (HTML, CSS, JS, & Media) will be hosted and globally distributed via the GitHub Pages CDN (Content Delivery Network). All of the data and dynamic requests for data (i.e., the back end) will be globally distributed and stateful via the Macrometa GDN (Global Data Network).
Macrometa is a geo-distributed stateful serverless platform designed from the ground up to be lightning-fast no matter where the client is located, optimized for both reads and writes, and elastically scalable. We will use it as a database for data collection and maintaining state and stream to subscribe to database updates for real-time action.
We will be using Gatsby to manage our app and deploy it to Github Pages. Let’s do this!
Intro
This demo uses the Macrometa c8db-source-plugin to get some of the data as markdown and then transform it to HTML to display directly in the browser and the Macrometa JSC8 SKD to keep an open socket for real-time fun and manage working with Macrometa’s API.
Once you’re logged in to Macrometa create a document collection called markdownContent. Then create a single document with title and content fields in markdown format. This creates your data model the app will be using for its static content.
Here’s an example of what the markdownContent collection should look like:
{ "title": "## Real-Time Polling Application", "content": "Full-Stack Geo-Distributed Serverless App Built with GatsbyJS and Macrometa!" }
content and title keys in the document are in the markdown format. Once they go through gatsby-source-c8db, data in title is converted to <h2></h2>, and content to <p></p>.
Now create a second document collection called polls. This is where the poll data will be stored.
In the polls collection each poll will be stored as a separate document. A sample document is mentioned below:
Your Macrometa login details, along with the collection to be used and markdown transformations, has to be provided in the application’s gatsby-config.js like below:
Under password you will notice that it says process.env.MM_PW, instead of putting your password there, we are going to create some .env files and make sure that those files are listed in our .gitignore file, so we don’t accidentally push our Macrometa password back to Github. In your root directory create .env.development and .env.production files.
You will only have one thing in each of those files: MM_PW='<your-password-here>'
Running the app locally
We have the frontend code already done, so you can fork the repo, set up your Macrometa account as described above, add your password as described above, and then deploy. Go ahead and do that and then I’ll walk you through how the app is set up so you can check out the code.
In the terminal of your choice:
Fork this repo and clone your fork onto your local machine
Run npm install
Once that’s done run npm run develop to start the local server. This will start local development server on http://localhost:<some_port> and the GraphQL server at http://localhost:<some_port>/___graphql
How to deploy app (UI) on GitHub Pages
Once you have the app running as expected in your local environment simply run npm run deploy!
Gatsby will automatically generate the static code for the site, create a branch called gh-pages, and deploy it to Github.
Now you can access your site at <your-github-username>.github.io/tutorial-jamstack-pollingapp
If your app isn‘t showing up there for some reason go check out your repo’s settings and make sure Github Pages is enabled and configured to run on your gh-pages branch.
Walking through the code
First, we made a file that loaded the Macrometa JSC8 Driver, made sure we opened up a socket to Macrometa, and then defined the various API calls we will be using in the app. Next, we made the config available to the whole app.
After that we wrote the functions that handle various front-end events. Here’s the code for handling a vote submission:
What if you have some content on one site and want to display that content on another site? We can do this in the browser no problem. We can fetch it, and plunk it onto the page.
Ajax, right? Ugh. Now we’re in client-side rendered site territory, which isn’t great for performance, speed, or resiliency.
What if we could fetch that content and stitch it into the main page on the server side? Server side isn’t the right word for it though. What if we could do it at the global CDN level? Do it at the edge, as they say. That’s what we’ve been doing at CodePen, so we can build pages with the lovely WordPress block editor but serve them on our main site.
Apple is well-known for the sleek animations on their product pages. For example, as you scroll down the page products may slide into view, MacBooks fold open and iPhones spin, all while showing off the hardware, demonstrating the software and telling interactive stories of how the products are used.
Just check out this video of the mobile web experience for the iPad Pro:
A lot of the effects that you see there aren’t created in just HTML and CSS. What then, you ask? Well, it can be a little hard to figure out. Even using the browser’s DevTools won’t always reveal the answer, as it often can’t see past a <canvas> element.
Let’s take an in-depth look at one of these effects to see how it’s made so you can recreate some of these magical effects in our own projects. Specifically, let’s replicate the AirPods Pro product page and the shifting light effect in the hero image.
The basic concept
The idea is to create an animation just like a sequence of images in rapid succession. You know, like a flip book! No complex WebGL scenes or advanced JavaScript libraries are needed.
By synchronizing each frame to the user’s scroll position, we can play the animation as the user scrolls down (or back up) the page.
Start with the markup and styles
The HTML and CSS for this effect is very easy as the magic happens inside the <canvas> element which we control with JavaScript by giving it an ID.
In CSS, we’ll give our document a height of 100vh and make our <body> 5⨉ taller than that to give ourselves the necessary scroll length to make this work. We’ll also match the background color of the document with the background color of our images.
The last thing we’ll do is position the <canvas>, center it, and limit the max-width and height so it does not exceed the dimensions of the viewport.
Right now, we are able to scroll down the page (even though the content does not exceed the viewport height) and our <canvas> stays at the top of the viewport. That’s all the HTML and CSS we need.
Let’s move on to loading the images.
Fetching the correct images
Since we’ll be working with an image sequence (again, like a flip book), we’ll assume the file names are numbered sequentially in ascending order (i.e. 0001.jpg, 0002.jpg, 0003.jpg, etc.) in the same directory.
We’ll write a function that returns the file path with the number of the image file we want, based off of the user’s scroll position.
const currentFrame = index => ( `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/$ {index.toString().padStart(4, '0')}.jpg` )
Since the image number is an integer, we’ll need to turn it in to a string and use padStart(4, '0') to prepend zeros in front of our index until we reach four digits to match our file names. So, for example, passing 1 into this function will return 0001.
That gives us a way to handle image paths. Here’s the first image in the sequence drawn on the <canvas> element:
As you can see, the first image is on the page. At this point, it’s just a static file. What we want is to update it based on the user’s scroll position. And we don’t merely want to load one image file and then swap it out by loading another image file. We want to draw the images on the <canvas> and update the drawing with the next image in the sequence (but we’ll get to that in just a bit).
We already made the function to generate the image filepath based on the number we pass into it so what we need to do now is track the user’s scroll position and determine the corresponding image frame for that scroll position.
Connecting images to the user’s scroll progress
To know which number we need to pass (and thus which image to load) in the sequence, we need to calculate the user’s scroll progress. We’ll make an event listener to track that and handle some math to calculate which image to load.
We need to know:
Where scrolling starts and ends
The user’s scroll progress (i.e. a percentage of how far the user is down the page)
The image that corresponds to the user’s scroll progress
We’ll use scrollTop to get the vertical scroll position of the element, which in our case happens to be the top of the document. That will serve as the starting point value. We’ll get the end (or maximum) value by subtracting the window height from the document scroll height. From there, we’ll divide the scrollTop value by the maximum value the user can scroll down, which gives us the user’s scroll progress.
Then we need to turn that scroll progress into an index number that corresponds with the image numbering sequence for us to return the correct image for that position. We can do this by multiplying the progress number by the number of frames (images) we have. We’ll use Math.floor() to round that number down and wrap it in Math.min() with our maximum frame count so it never exceeds the total number of frames.
We now know which image we need to draw as the user’s scroll progress changes. This is where the magic of <canvas> comes into play. <canvas> has many cool features for building everything from games and animations to design mockup generators and everything in between!
One of those features is a method called requestAnimationFrame that works with the browser to update <canvas> in a way we couldn’t do if we were working with straight image files instead. This is why I went with a <canvas> approach instead of, say, an <img> element or a <div> with a background image.
requestAnimationFrame will match the browser refresh rate and enable hardware acceleration by using WebGL to render it using the device’s video card or integrated graphics. In other words, we’ll get super smooth transitions between frames — no image flashes!
Let’s call this function in our scroll event listener to swap images as the user scrolls up or down the page. requestAnimationFrame takes a callback argument, so we’ll pass a function that will update the image source and draw the new image on the <canvas>:
We’re bumping up the frameIndex by 1 because, while the image sequence starts at 0001.jpg, our scroll progress calculation starts actually starts at 0. This ensures that the two values are always aligned.
The callback function we pass to update the image looks like this:
We pass the frameIndex into the function. That sets the image source with the next image in the sequence, which is drawn on our <canvas> element.
Even better with image preloading
We’re technically done at this point. But, come on, we can do better! For example, scrolling quickly results in a little lag between image frames. That’s because every new image sends off a new network request, requiring a new download.
We should try preloading the images new network requests. That way, each frame is already downloaded, making the transitions that much faster, and the animation that much smoother!
All we’ve gotta do is loop through the entire sequence of images and load ‘em up:
const frameCount = 148; const preloadImages = () => { for (let i = 1; i < frameCount; i++) { const img = new Image(); img.src = currentFrame(i); } }; preloadImages();
Demo!
A quick note on performance
While this effect is pretty slick, it’s also a lot of images. 148 to be exact.
No matter much we optimize the images, or how speedy the CDN is that serves them, loading hundreds of images will always result in a bloated page. Let’s say we have multiple instances of this on the same page. We might get performance stats like this:
That might be fine for a high-speed internet connection without tight data caps, but we can’t say the same for users without such luxuries. It’s a tricky balance to strike, but we have to be mindful of everyone’s experience — and how our decisions affect them.
A few things we can do to help strike that balance include:
Loading a single fallback image instead of the entire image sequence
Creating sequences that use smaller image files for certain devices
Allowing the user to enable the sequence, perhaps with a button that starts and stops the sequence
Apple employs the first option. If you load the AirPods Pro page on a mobile device connected to a slow 3G connection and, hey, the performance stats start to look a whole lot better:
Yeah, it’s still a heavy page. But it’s a lot lighter than what we’d get without any performance considerations at all. That’s how Apple is able to get get so many complex sequences onto a single page.
Further reading
If you are interested in how these image sequences are generated, a good place to start is the Lottie library by AirBnB. The docs take you through the basics of generating animations with After Effects while providing an easy way to include them in projects.
In this tutorial, we’ll cover how to make taxonomy pages with Gatsby with structured content from Sanity.io. You will learn how to use Gatsby’s Node creation APIs to add fields to your content types in Gatsby’s GraphQL API. Specifically, we’re going to create category pages for the Sanity’s blog starter.
That being said, there is nothing Sanity-specific about what we’re covering here. You’re able to do this regardless of which content source you may have. We’re just reaching for Sanity.io for the sake of demonstration.
Get up and running with the blog
If you want to follow this tutorial with your own Gatsby project, go ahead and skip to the section for creating a new page template in Gatsby. If not, head over to sanity.io/create and launch the Gatsby blog starter. It will put the code for Sanity Studio and the Gatsby front-end in your GitHub account and set up the deployment for both on Netlify. All the configuration, including example content, will be in place so that you can dive right into learning how to create taxonomy pages.
Once the project is iniated, make sure to clone the new repository on GitHub to local, and install the dependencies:
git clone git@github.com:username/your-repository-name.git cd your-repository-name npm i
If you want to run both Sanity Studio (the CMS) and the Gatsby front-end locally, you can do so by running the command npm run dev in a terminal from the project root. You can also cd into the web folder and just run Gatsby with the same command.
You should also install the Sanity CLI and log in to your account from the terminal: npm i -g @sanity/cli && sanity login. This will give you tooling and useful commands to interact with Sanity projects. You can add the --help flag to get more information on its functionality and commands.
We will be doing some customization to the gatsby-node.js file. To see the result of the changes, restart Gatsby’s development server. This is done in most systems by hitting CTRL + C in the terminal and running npm run dev again.
Getting familiar with the content model
Look into the /studio/schemas/documents folder. There are schema files for our main content types: author, category, site settings, and posts. Each of the files exports a JavaScript object that defines the fields and properties of these content types. Inside of post.js is the field definition for categories:
This will create an array field with reference objects to category documents. Inside of the blog’s studio it will look like this:
An array field with references to category documents in the blog studio
Adding slugs to the category type
Head over to /studio/schemas/documents/category.js. There is a simple content model for a category that consists of a title and a description. Now that we’re creating dedicated pages for categories, it would be handy to have a slug field as well. We can define that in the schema like this:
// studio/schemas/documents/category.js export default { name: 'category', type: 'document', title: 'Category', fields: [ { name: 'title', type: 'string', title: 'Title' }, { name: 'slug', type: 'slug', title: 'Slug', options: { // add a button to generate slug from the title field source: 'title' } }, { name: 'description', type: 'text', title: 'Description' } ] }
Now that we have changed the content model, we need to update the GraphQL schema definition as well. Do this by executing npm run graphql-deploy (alternatively: sanity graphql deploy) in the studio folder. You will get warnings about breaking changes, but since we are only adding a field, you can proceed without worry. If you want the field to accessible in your studio on Netlify, check the changes into git (with git add . && git commit -m"add slug field") and push it to your GitHub repository (git push origin master).
Now we should go through the categories and generate slugs for them. Remember to hit the publish button to make the changes accessible for Gatsby! And if you were running Gatsby’s development server, you’ll need to restart that too.
Quick sidenote on how the Sanity source plugin works
When starting Gatsby in development or building a website, the source plugin will first fetch the GraphQL Schema Definitions from Sanity deployed GraphQL API. The source plugin uses this to tell Gatsby which fields should be available to prevent it from breaking if the content for certain fields happens to disappear. Then it will hit the project’s export endpoint, which streams all the accessible documents to Gatsby’s in-memory datastore.
In order words, the whole site is built with two requests. Running the development server, will also set up a listener that pushes whatever changes come from Sanity to Gatsby in real-time, without doing additional API queries. If we give the source plugin a token with permission to read drafts, we’ll see the changes instantly. This can also be experienced with Gatsby Preview.
Adding a category page template in Gatsby
Now that we have the GraphQL schema definition and some content ready, we can dive into creating category page templates in Gatsby. We need to do two things:
Tell Gatsby to create pages for the category nodes (that is Gatsby’s term for “documents”).
Give Gatsby a template file to generate the HTML with the page data.
Begin by opening the /web/gatsby-node.js file. Code will already be here that can be used to create the blog post pages. We’ll largely leverage this exact code, but for categories. Let’s take it step-by-step:
Between the createBlogPostPages function and the line that starts with exports.createPages, we can add the following code. I’ve put in comments here to explain what’s going on:
// web/gatsby-node.js // ... async function createCategoryPages (graphql, actions) { // Get Gatsby‘s method for creating new pages const {createPage} = actions // Query Gatsby‘s GraphAPI for all the categories that come from Sanity // You can query this API on http://localhost:8000/___graphql const result = await graphql(`{ allSanityCategory { nodes { slug { current } id } } } `) // If there are any errors in the query, cancel the build and tell us if (result.errors) throw result.errors // Let‘s gracefully handle if allSanityCatgogy is null const categoryNodes = (result.data.allSanityCategory || {}).nodes || [] categoryNodes // Loop through the category nodes, but don't return anything .forEach((node) => { // Desctructure the id and slug fields for each category const {id, slug = {}} = node // If there isn't a slug, we want to do nothing if (!slug) return // Make the URL with the current slug const path = `/categories/$ {slug.current}` // Create the page using the URL path and the template file, and pass down the id // that we can use to query for the right category in the template file createPage({ path, component: require.resolve('./src/templates/category.js'), context: {id} }) }) }
Last, this function is needed at the bottom of the file:
// /web/gatsby-node.js // ... exports.createPages = async ({graphql, actions}) => { await createBlogPostPages(graphql, actions) await createCategoryPages(graphql, actions) // <= add the function here }
Now that we have the machinery to create the category page node in place, we need to add a template for how it actually should look in the browser. We’ll base it on the existing blog post template to get some consistent styling, but keep it fairly simple in the process.
// /web/src/templates/category.js import React from 'react' import {graphql} from 'gatsby' import Container from '../components/container' import GraphQLErrorList from '../components/graphql-error-list' import SEO from '../components/seo' import Layout from '../containers/layout' export const query = graphql` query CategoryTemplateQuery($ id: String!) { category: sanityCategory(id: {eq: $ id}) { title description } } ` const CategoryPostTemplate = props => { const {data = {}, errors} = props const {title, description} = data.category || {} return ( <Layout> <Container> {errors && <GraphQLErrorList errors={errors} />} {!data.category && <p>No category data</p>} <SEO title=How to Make Taxonomy Pages With Gatsby and Sanity.io description={description} /> <article> <h1>Category: How to Make Taxonomy Pages With Gatsby and Sanity.io</h1> <p>{description}</p> </article> </Container> </Layout> ) } export default CategoryPostTemplate
We are using the ID that was passed into the context in gatsby-node.js to query the category content. Then we use it to query the title and description fields that are on the category type. Make sure to restart with npm run dev after saving these changes, and head over to localhost:8000/categories/structured-content in the browser. The page should look something like this:
A barebones category page
Cool stuff! But it would be even cooler if we actually could see what posts that belong to this category, because, well, that’s kinda the point of having categories in the first place, right? Ideally, we should be able to query for a “pages” field on the category object.
Before we learn how to that, we need to take a step back to understand how Sanity’s references work.
Querying Sanity’s references
Even though we’re only defining the references in one type, Sanity’s datastore will index them “bi-directionally.” That means creating a reference to the “Structured content” category document from a post lets Sanity know that the category has these incoming references and will keep you from deleting it as long as the reference exists (references can be set as “weak” to override this behavior). If we use GROQ, we can query categories and join posts that have them like this (see the query and result in action on groq.dev):
This ouputs a data structure that lets us make a simple category post template:
[ { "_id": "39d2ca7f-4862-4ab2-b902-0bf10f1d4c34", "_type": "category", "title": "Structured content", "posts": [ { "title": "Exploration powered by structured content", "slug": { "_type": "slug", "current": "exploration-powered-by-structured-content" } }, { "title": "My brand new blog powered by Sanity.io", "slug": { "_type": "slug", "current": "my-brand-new-blog-powered-by-sanity-io" } } ] }, // ... more entries ]
That’s fine for GROQ, what about GraphQL?
Here‘s the kicker: As of yet, this kind of query isn’t possible with Gatsby’s GraphQL API out of the box. But fear not! Gatsby has a powerful API for changing its GraphQL schema that lets us add fields.
Using createResolvers to edit Gatsby’s GraphQL API
Gatsby holds all the content in memory when it builds your site and exposes some APIs that let us tap into how it processes this information. Among these are the Node APIs. It’s probably good to clarify that when we are talking about “node” in Gatsby — not to be confused with Node.js. The creators of Gatsby have borrowed “edges and nodes” from Graph theory where “edges” are the connections between the “nodes” which are the “points” where the actual content is located. Since an edge is a connection between nodes, it can have a “next” and “previous” property.
The edges with next and previous, and the node with fields in GraphQL’s API explorer
The Node APIs are used by plugins first and foremost, but they can be used to customize how our GraphQL API should work as well. One of these APIs is called createResolvers. It’s fairly new and it lets us tap into how a type’s nodes are created so we can make queries that add data to them.
Let’s use it to add the following logic:
Check for ones with the SanityCategory type when creating the nodes.
If a node matches this type, create a new field called posts and set it to the SanityPost type.
Then run a query that filters all posts that has lists a category that matches the current category’s ID.
If there are matching IDs, add the content of the post nodes to this field.
Add the following code to the /web/gatsby-node.js file, either below or above the code that’s already in there:
Now, let’s restart Gatsby’s development server. We should be able to find a new field for posts inside of the sanityCategory and allSanityCategory types.
Adding the list of posts to the category template
Now that we have the data we need, we can return to our category page template (/web/src/templates/category.js) and add a list with links to the posts belonging to the category.
// /web/src/templates/category.js import React from 'react' import {graphql, Link} from 'gatsby' import Container from '../components/container' import GraphQLErrorList from '../components/graphql-error-list' import SEO from '../components/seo' import Layout from '../containers/layout' // Import a function to build the blog URL import {getBlogUrl} from '../lib/helpers' // Add “posts” to the GraphQL query export const query = graphql` query CategoryTemplateQuery($ id: String!) { category: sanityCategory(id: {eq: $ id}) { title description posts { _id title publishedAt slug { current } } } } ` const CategoryPostTemplate = props => { const {data = {}, errors} = props // Destructure the new posts property from props const {title, description, posts} = data.category || {} return ( <Layout> <Container> {errors && <GraphQLErrorList errors={errors} />} {!data.category && <p>No category data</p>} <SEO title=How to Make Taxonomy Pages With Gatsby and Sanity.io description={description} /> <article> <h1>Category: How to Make Taxonomy Pages With Gatsby and Sanity.io</h1> <p>{description}</p> {/* If there are any posts, add the heading, with the list of links to the posts */} {posts && ( <React.Fragment> <h2>Posts</h2> <ul> { posts.map(post => ( <li key={post._id}> <Link to={getBlogUrl(post.publishedAt, post.slug)}>{post.title}</Link> </li>)) } </ul> </React.Fragment>) } </article> </Container> </Layout> ) } export default CategoryPostTemplate
This code will produce this simple category page with a list of linked posts – just liked we wanted!
Go make taxonomy pages!
We just completed the process of creating new page types with custom page templates in Gatsby. We covered one of Gatsby’s Node APIs called createResolver and used it to add a new posts field to the category nodes.
This should give you what you need to make other types of taxonomy pages! Do you have multiple authors on your blog? Well, you can use the same logic to create author pages. The interesting thing with the GraphQL filter is that you can use it to go beyond the explicit relationship made with references. It can also be used to match other fields using regular expressions or string comparisons. It’s fairly flexible!
Dave discovered that Feedbin can also produce an RSS feed for all your likes. Likes is a feature of Feedbin, and fortunately also NetNewsWire, which syncs the likes back to Feedbin. You have to flip a settings switch in Feedbin, but then you get a URL for your likes. Here’s mine.
Unfortunately, the feed isn’t CORS ready, so you’ll have to run it through a proxy — but it’s doable.
Those are both JavaScript-powered. Here’s how they work:
I haven’t decided if I’m going to toss one up somewhere. If I do I’ll probably do it with WordPress Transients as I’ve had some experience with that (that’s how the jobs page works, for example).
A clever idea from Tom Hicks combining MutationObserver (which can “observe” changes to elements like when their attributes, text, or children change) and the Web Audio API for creating sounds. Plop this code into the console on a page where you’d like to listen to essentially any DOM change to hear it doing stuff.
I played with it on my serverless site because it’s an SPA so there is plenty of DOM activity as you navigate around.
Looks like Tom is experimenting with other audio… what should we call them? Auralizations? Like this sweep-swoop one. There is already a browser extension for it, which includes sounds for network activity happening.
In this week’s news: Firefox gets strict, Opera goes to the dark side, and Chrome plans to let web apps run in the background.
Let’s get into the news.
Firefox for Android will block tracking content
Mozilla has announced that the upcoming revamped Firefox for Android (currently available in a test version under the name “Firefox Preview”) will include strict tracking protection by default.
On the phone or tablet, most users care much more about performance and blocking of annoyances compared to desktop. Users are more forgiving when a site doesn’t load exactly like it’s meant to. So we decided that while Firefox for desktop’s default mode is “Standard,” Firefox Preview will use “Strict” mode.
Strict tracking protection additionally blocks “tracking content”: ads, videos, and other content with tracking code.
Opera adds option that renders all websites in dark mode
Opera for Android has added a “Dark web pages” option that renders all websites in dark mode. If a website does not provide dark mode styles (via the CSS prefers-color-scheme media feature), Opera applies its own “clever CSS changes” to render the site in dark mode regardless.
Google intends to ship Periodic Background Sync in the next version of Chrome (early next year). This feature will enable installed web apps to run background tasks at periodic intervals with network connectivity.
Chrome’s implementation restricts the API to installed web apps. Chrome grants the permission on behalf of the user for any installed web app. The API is not available outside of installed PWAs.
Apple and Mozilla are currently opposed to this API. At Mozilla, there are opinions that the feature is “harmful in its current state,” while Apple states multiple privacy and security risks, this leads users to look out for other options, such as those found at https://www.fortinet.com/products/web-application-firewall/fortiweb.
The typical journey for a person browsing a website: view a page, click a link, browser loads new page. That’s assuming no funny business like a Single Page App, which still follows that journey, but the browser doesn’t load a new page — the client fakes it for the sake of a snappier transition.
What if you could load that new page before the person clicks the link so that, when they do, the loading of that next page is much faster? There are two notable projects that try to help with that:
quicklink: detects visible links, waits for the browser to be idle and if it isn’t on slow connection, it prefetches those links.
instant.page: if you hover over a link for 65ms, it preloads that link. The new Version 2 allows you to configure of the time delay or whether to wait for a click or press before preloading.
Combine those things with technological improvements like paint holding, and building a SPA architecture just for the speed benefits may become unnecessary (though it may still be desirable for other reasons, like code-splitting, putting the onus of routing onto front-end developers, etc.).
Chrome on Android’s Data Saver feature helps by automatically optimizing web pages to make them load faster. When users are facing network or data constraints, Data Saver may reduce data use by up to 90% and load pages two times faster, and by making pages load faster, a larger fraction of pages actually finish loading on slow networks. Now, we are securely extending performance improvements beyond HTTP pages to HTTPS pages and providing direct feedback to the developers who want it.
To show users when a page has been optimized, Chrome now shows in the URL bar that a Lite version of the page is being displayed.
All of this is pretty neat but I think the name Lite Pages is a little confusing as it’s in no way related to AMP and Tim Kadlec makes that clear in his notes about the new feature:
Lite pages are also in no way related to AMP. AMP is a framework you have to build your site in to reap any benefit from. Lite pages are optimizations and interventions that get applied to your current site. Google’s servers are still involved, by as a proxy service forwarding the initial request along. Your URL’s aren’t tampered with in any way.
A quick glance at this seems great! We don’t have to give up ownership of our URLs, like with AMP, and we don’t have to develop with a proprietary technology — we can let Chrome be Chrome and do any performance things that it wants to do without turning anything on or off or adding JavaScript.
But wait! What kind of optimizations does a Lite Page make and how do they affect our sites? So far, it can disable scripts, replace images with placeholders and stop the loading of certain resources, although this is all subject to change in the future, I guess.
The optimizations only take effect when the loading experience for users is particularly bad, as the announcement blog post states:
…they are applied when the network’s effective connection type is “2G” or “slow-2G,” or when Chrome estimates the page load will take more than 5 seconds to reach first contentful paint given current network conditions and device capabilities.
It’s probably important to remember that the reason why Google is doing this isn’t to break our designs or mess with our websites — they’re doing this because there are serious performance concerns with the web, and those concerns aren’t limited to developing nations.