Debugging "max serverless size exceeded" in Next.js

How to debug the "A Serverless Function has exceeded the unzipped maximum size of 250 MB" error in Next.js & Vercel

So the other day, I had the pleasure of running into this lovely error when deploying my Next.js website to Vercel:

The nice thing about this error is that you only get it when you deploy your website, and only after the issue has been compounding for a while.

The error message is pretty clear, as it tells you which serverless function exceeds the limit, and what the largest dependencies are, but in my case, it was a bit of a mystery since all of my functions were exceeding the limit, and the largest dependencies were all the same.

Here's the build output for reference:

log
[11:18:14.653] Running build in Washington, D.C., USA (East) – iad1
[11:18:14.874] Cloning github.com/ykadosh/yoavik.com (Branch: master, Commit: 99c264e)
[11:18:14.982] Previous build caches not available
[11:18:29.516] Cloning completed: 14.641s
[11:18:29.925] Running "vercel build"
[11:18:30.352] Vercel CLI 41.2.2
[11:18:30.756] Installing dependencies...
[11:18:38.927] npm warn deprecated babel-eslint@10.1.0: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.
[11:18:51.662]
[11:18:51.662] added 852 packages in 21s
[11:18:51.663]
[11:18:51.663] 314 packages are looking for funding
[11:18:51.663]   run `npm fund` for details
[11:18:51.688] Detected Next.js version: 14.2.15
[11:18:51.700] Running "npm run build"
[11:18:51.899]
[11:18:51.899] > yoavik.com@1.0.0 build
[11:18:51.899] > next build
[11:18:51.900]
[11:18:52.860] Attention: Next.js now collects completely anonymous telemetry regarding usage.
[11:18:52.861] This information is used to shape Next.js' roadmap and prioritize features.
[11:18:52.861] You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
[11:18:52.861] https://nextjs.org/telemetry
[11:18:52.861]
[11:18:52.919]   ▲ Next.js 14.2.15
[11:18:52.920]
[11:18:52.920]    Linting and checking validity of types ...
[11:18:53.167]    Creating an optimized production build ...
[11:19:36.689]  ✓ Compiled successfully
[11:19:36.689]    Collecting page data ...
[11:19:38.558]    Generating static pages (0/40) ...
[11:19:39.262]    Generating static pages (10/40)
[11:19:40.096]    Generating static pages (20/40)
[11:19:40.926]    Generating static pages (30/40)
[11:19:42.285]  ✓ Generating static pages (40/40)
[11:19:49.504]    Finalizing page optimization ...
[11:19:49.505]    Collecting build traces ...
[11:19:49.664]  ⚠ `config.analyticsId` is deprecated and will be removed in next major version. Read more: https://nextjs.org/docs/messages/deprecated-analyticsid
[11:19:53.611]
[11:19:53.616] Route (pages)                              Size     First Load JS
[11:19:53.617] ┌ ● /                                      10.5 kB         150 kB
[11:19:53.617] ├   └ css/77bab511c8c3e21a.css             1.29 kB
[11:19:53.617] ├   /_app                                  0 B            97.1 kB
[11:19:53.617] ├ ○ /404                                   3.84 kB         109 kB
[11:19:53.618] ├ ƒ /api/likes/[id]                        0 B            97.1 kB
[11:19:53.618] ├ ƒ /api/likes/increment                   0 B            97.1 kB
[11:19:53.618] ├ ƒ /api/newsletter/add                    0 B            97.1 kB
[11:19:53.618] ├ ƒ /api/twitter/popular                   0 B            97.1 kB
[11:19:53.618] ├ ƒ /api/views/[id]                        0 B            97.1 kB
[11:19:53.619] ├ ● /experiments                           1.46 kB         115 kB
[11:19:53.619] ├ ● /fabrication                           1.2 kB          119 kB
[11:19:53.623] ├ ○ /fabrication/band-saw-cabinet          2.05 kB         185 kB
[11:19:53.623] ├ ○ /fabrication/book-shelves              4.05 kB         187 kB
[11:19:53.623] ├ ○ /fabrication/drill-press-cabinet       4.76 kB         188 kB
[11:19:53.623] ├ ○ /fabrication/friendship-bench          5.9 kB          189 kB
[11:19:53.623] ├   └ css/1beea7a62195c237.css             2.68 kB
[11:19:53.623] ├ ○ /fabrication/lego-cabinet              6.81 kB         190 kB
[11:19:53.623] ├ ○ /fabrication/office-desk               4.7 kB          188 kB
[11:19:53.624] ├ ○ /fabrication/planter-box               5.46 kB         189 kB
[11:19:53.624] ├ ○ /fabrication/rabbit-hutch              4.01 kB         187 kB
[11:19:53.624] ├ ○ /fabrication/tic-tac-toe               2.91 kB         186 kB
[11:19:53.624] ├ ○ /fabrication/waterfall-tables          8.33 kB         191 kB
[11:19:53.624] ├ ○ /fabrication/wine-glass-holder         1.89 kB         185 kB
[11:19:53.624] ├ ○ /fabrication/wooden-chandelier         3.71 kB         187 kB
[11:19:53.624] ├ ○ /fabrication/wooden-playhouse          10.4 kB         193 kB
[11:19:53.624] ├ ○ /fabrication/workbench                 4.34 kB         187 kB
[11:19:53.625] ├ ○ /fabrication/yard-fence                4.52 kB         188 kB
[11:19:53.625] ├ ● /posts                                 1.39 kB         115 kB
[11:19:53.625] ├   └ css/d3ec71a54ac08158.css             679 B
[11:19:53.625] ├ ○ /posts/canvas-path-animation           1.63 kB         176 kB
[11:19:53.625] ├ ○ /posts/embedding-tweets                2.69 kB         178 kB
[11:19:53.625] ├ ○ /posts/generate-toc-mdx                5.4 kB          180 kB
[11:19:53.625] ├ ○ /posts/how-i-built-my-blog             648 B           175 kB
[11:19:53.626] ├ ○ /posts/playground                      4.43 kB         379 kB
[11:19:53.626] ├   └ css/d1c9d54da62da662.css             2.28 kB
[11:19:53.626] ├ ○ /posts/randomizing-svg-paths           821 B           176 kB
[11:19:53.626] ├ ƒ /rss.xml                               244 B          97.4 kB
[11:19:53.626] ├ ƒ /search                                3.71 kB         122 kB
[11:19:53.626] ├   └ css/f7900d16be8a4b87.css             679 B
[11:19:53.626] ├ ƒ /sitemap.txt                           248 B          97.4 kB
[11:19:53.627] ├ ● /snippets                              2.17 kB         116 kB
[11:19:53.627] ├ ○ /snippets/3d-text                      2.61 kB         177 kB
[11:19:53.627] ├   └ css/de6589cdc3b937a2.css             3.24 kB
[11:19:53.627] ├ ○ /snippets/client-only                  1.18 kB         176 kB
[11:19:53.627] ├ ○ /snippets/codepen                      2.36 kB         177 kB
[11:19:53.627] ├ ○ /snippets/image-with-fallback          6.82 kB         381 kB
[11:19:53.628] ├ ○ /snippets/media-query-mixin            1.98 kB         177 kB
[11:19:53.628] ├ ○ /snippets/mouse-tracker                5.38 kB         380 kB
[11:19:53.628] ├ ○ /snippets/use-animation-frame          1.08 kB         176 kB
[11:19:53.628] ├ ○ /snippets/use-document-event           2.28 kB         177 kB
[11:19:53.628] ├ ○ /snippets/use-global-state             6.63 kB         381 kB
[11:19:53.628] ├ ○ /snippets/use-intersection-observer    3.07 kB         178 kB
[11:19:53.628] ├ ○ /snippets/use-perishable               6.95 kB         381 kB
[11:19:53.629] └ ○ /snippets/use-value                    4.05 kB         378 kB
[11:19:53.629] + First Load JS shared by all              105 kB
[11:19:53.629]   ├ chunks/framework-945b357d4a851f4b.js   44.9 kB
[11:19:53.629]   ├ chunks/main-5ae912dde92706cf.js        32.6 kB
[11:19:53.629]   ├ chunks/pages/_app-31da04a866cf9ef8.js  18 kB
[11:19:53.629]   └ other shared chunks (total)            9.13 kB
[11:19:53.630]
[11:19:53.634] ○  (Static)   prerendered as static content
[11:19:53.634] ●  (SSG)      prerendered as static HTML (uses getStaticProps)
[11:19:53.634] ƒ  (Dynamic)  server-rendered on demand
[11:19:53.634]
[11:19:53.789] Traced Next.js server files in: 33.243ms
[11:19:56.275] Warning: Max serverless function size of 250 MB uncompressed reached
[11:19:56.276] Serverless Function's page: experiments.js
[11:19:56.281] Large Dependencies                     Uncompressed size
[11:19:56.282] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.282] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.282] pages/fabrication/planter-box                   24.06 MB
[11:19:56.282] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.282] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.282] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.282] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.282] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.283] pages/fabrication/workbench                     11.41 MB
[11:19:56.283] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.283] pages/fabrication/office-desk                   10.37 MB
[11:19:56.283] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.283] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.283] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.283] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.283] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.284] node_modules/next/dist                         961.97 KB
[11:19:56.284]
[11:19:56.284] All dependencies                               254.39 MB
[11:19:56.284] Serverless Function's page: fabrication.js
[11:19:56.289] Large Dependencies                     Uncompressed size
[11:19:56.289] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.289] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.289] pages/fabrication/planter-box                   24.06 MB
[11:19:56.289] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.289] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.289] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.289] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.290] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.290] pages/fabrication/workbench                     11.41 MB
[11:19:56.290] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.290] pages/fabrication/office-desk                   10.37 MB
[11:19:56.290] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.290] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.290] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.290] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.290] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.291] node_modules/next/dist                         961.97 KB
[11:19:56.291]
[11:19:56.291] All dependencies                               254.41 MB
[11:19:56.291] Serverless Function's page: index.js
[11:19:56.294] Large Dependencies                     Uncompressed size
[11:19:56.294] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.294] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.295] pages/fabrication/planter-box                   24.06 MB
[11:19:56.295] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.295] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.295] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.295] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.296] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.296] pages/fabrication/workbench                     11.41 MB
[11:19:56.296] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.297] pages/fabrication/office-desk                   10.37 MB
[11:19:56.297] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.297] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.297] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.297] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.297] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.297] node_modules/next/dist                         961.97 KB
[11:19:56.298]
[11:19:56.298] All dependencies                               254.84 MB
[11:19:56.300] Serverless Function's page: posts.js
[11:19:56.300] Large Dependencies                     Uncompressed size
[11:19:56.301] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.301] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.301] pages/fabrication/planter-box                   24.06 MB
[11:19:56.301] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.301] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.301] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.301] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.302] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.302] pages/fabrication/workbench                     11.41 MB
[11:19:56.302] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.302] pages/fabrication/office-desk                   10.37 MB
[11:19:56.302] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.302] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.302] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.302] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.303] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.303] node_modules/next/dist                         961.97 KB
[11:19:56.303]
[11:19:56.303] All dependencies                               254.39 MB
[11:19:56.305] Serverless Function's page: rss.xml.js
[11:19:56.307] Large Dependencies                     Uncompressed size
[11:19:56.307] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.307] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.307] pages/fabrication/planter-box                   24.06 MB
[11:19:56.307] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.307] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.307] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.308] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.308] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.308] pages/fabrication/workbench                     11.41 MB
[11:19:56.308] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.308] pages/fabrication/office-desk                   10.37 MB
[11:19:56.308] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.308] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.308] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.309] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.309] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.309] node_modules/next/dist                         961.97 KB
[11:19:56.309]
[11:19:56.309] All dependencies                               254.43 MB
[11:19:56.314] Serverless Function's page: search.js
[11:19:56.329] Large Dependencies                     Uncompressed size
[11:19:56.330] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.330] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.330] pages/fabrication/planter-box                   24.06 MB
[11:19:56.330] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.330] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.331] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.331] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.331] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.331] pages/fabrication/workbench                     11.41 MB
[11:19:56.331] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.331] pages/fabrication/office-desk                   10.37 MB
[11:19:56.331] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.331] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.332] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.332] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.332] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.332] node_modules/next/dist                         961.97 KB
[11:19:56.332]
[11:19:56.332] All dependencies                               254.41 MB
[11:19:56.333] Serverless Function's page: sitemap.txt.js
[11:19:56.337] Large Dependencies                     Uncompressed size
[11:19:56.337] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.338] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.338] pages/fabrication/planter-box                   24.06 MB
[11:19:56.338] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.338] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.338] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.338] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.338] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.338] pages/fabrication/workbench                     11.41 MB
[11:19:56.339] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.339] pages/fabrication/office-desk                   10.37 MB
[11:19:56.339] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.339] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.339] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.339] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.339] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.339] node_modules/next/dist                         961.97 KB
[11:19:56.340]
[11:19:56.340] All dependencies                               254.34 MB
[11:19:56.342] Serverless Function's page: snippets.js
[11:19:56.346] Large Dependencies                     Uncompressed size
[11:19:56.346] pages/fabrication/wooden-playhouse              63.08 MB
[11:19:56.346] pages/fabrication/waterfall-tables              25.86 MB
[11:19:56.346] pages/fabrication/planter-box                   24.06 MB
[11:19:56.346] pages/fabrication/lego-cabinet                  23.41 MB
[11:19:56.346] pages/fabrication/drill-press-cabinet           21.03 MB
[11:19:56.346] pages/fabrication/yard-fence                    13.71 MB
[11:19:56.346] pages/fabrication/friendship-bench              13.12 MB
[11:19:56.346] pages/fabrication/wooden-chandelier              12.3 MB
[11:19:56.346] pages/fabrication/workbench                     11.41 MB
[11:19:56.346] pages/fabrication/rabbit-hutch                  10.55 MB
[11:19:56.346] pages/fabrication/office-desk                   10.37 MB
[11:19:56.346] pages/fabrication/tic-tac-toe                    8.67 MB
[11:19:56.346] pages/fabrication/book-shelves                   7.75 MB
[11:19:56.346] pages/fabrication/band-saw-cabinet               2.27 MB
[11:19:56.346] node_modules/react-dom/cjs                       1.64 MB
[11:19:56.346] pages/fabrication/wine-glass-holder              1.12 MB
[11:19:56.346] node_modules/next/dist                         961.97 KB
[11:19:56.346]
[11:19:56.347] All dependencies                               254.39 MB
[11:19:56.347] Max serverless function size was exceeded for 8 functions
[11:19:56.359] Created all serverless functions in: 2.570s
[11:19:56.396] Collected static files (public/, static/, .next/static): 31.907ms
[11:19:56.530] Build Completed in /vercel/output [1m]
[11:19:56.920] Deploying outputs...
[11:21:29.071] Error: A Serverless Function has exceeded the unzipped maximum size of 250 MB. : https://vercel.link/serverless-function-size
[11:21:32.679]

As you can see, the largest dependencies are all coming from the fabrication directory, because there are a lot of images in there, and the serverless function is trying to bundle them all. But why?

Debugging locally

So the first step in debugging this issue is to try and reproduce it locally. According to the build output, the command that failed was vercel build, so I tried running it locally to see if I could reproduce the issue.

This was the first time I was running this command, so I had to install the Vercel CLI first:

bash
npm i -g vercel

After installing the CLI, I ran the build command, and got the same output as the one above. I focused on the snippets.js page, since it was the last one. I opened the file and started by removing all of the imports, and then adding them back one by one to see which one was causing the issue. Each time I ran the vercel build command to see if snippets.js was still appearing in the output.

Once I found the problematic import, I opened that file and repeated the same process. It was a bit of a rabbit hole, but eventually I found the issue. It was this function:

js
export const getPosts = async () => {
    const metas = require.context('../pages/posts', true, /\.\/[^/]+\/index\.meta\.js$/);
    const posts = [];
    for (let path of metas.keys()) {
        const { meta } = await metas(path);
        if (meta.published || process.env.NODE_ENV === 'development') {
            meta.url = `/posts/${path.replace('/index.meta.js', '')}`;
            const PAGES_DIR = join(process.cwd(), 'pages');
            const content = await fs.readFile(join(PAGES_DIR, 'posts', path.replace('meta.js', 'page.mdx')), 'utf8');
            meta.readingTime = await getReadingTime(content);
            posts.push(meta);
        }
    }
    return posts;
}

The purpose of this function is to read the metadata of all the posts in the pages/posts directory, and to enrich it with additional information like reading time. The reading time is calculated based on the size of the content, which we get by reading the file. To read the file, I needed the absolute path, which I was calculating by joining the current working directory (process.cwd()) with the relative path of the file. This is a dynamic import with a variable path, and that was the root cause of the issue.

The problem

Dynamic imports with variable paths are technically impossible, because the bundling is done at build time, while the path is only known at runtime. This means that Webpack doesn't know which files to include in the bundle, and it ends up including all the files in the directory.

In my case, this was a fully dynamic path, so it included all the directories, including the fabrication directory, which contains a lot of images. That's why the serverless function was exceeding the limit.

From Webpack's documentation:

It is not possible to use a fully dynamic import statement, such as import(foo). Because foo could potentially be any path to any file in your system or project. The import() must contain at least some information about where the module is located. Bundling can be limited to a specific directory or set of files so that when you are using a dynamic expression - every module that could potentially be requested on an import() call is included.

https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import

The solution is to limit the scope of the dynamic import to a specific directory, by providing the beginning of the path. So by changing a single line, the issue was fixed:

js
const content = await fs.readFile(join('pages/posts', path.replace('meta.js', 'page.mdx')), 'utf8');

Now, only the files under the pages/posts directory are included in the bundle, and the serverless function is no longer exceeding the limit. But it was only a matter of time before I ran into the same issue again, because I was still using a dynamic import to read the metadata files.

Over-engineering

This brings me to a broader conclusion about over-engineering. The reason I had to use require.context and fs.readFile is because I wanted to avoid having to import and enrich all the files manually. I wanted to be able to add a new post, and have the rest of the code automatically detect it.

But in reality, this is not a problem at all. I can just import the file manually when I add a new post, and if I forget to do it, I'll see that it's missing in my website. This is a much simpler solution, and it doesn't require me to use any hacks like require.context.

While the original solution was supposed to save me time, it ended up costing me a lot of time in debugging.

If you still want to automatically include all files in a directory, or produce some output based on file structure, you can run a script at build time that will generate an index file with all the imports, or even have it read the content and generate the metadata automatically.

Spread the word about this awesome stuff!

A newsletter for front-end web developers

Stay up-to-date with my latest articles, experiments, tools, and much more!

Issued monthly (or so). No spam. Unsubscribe any time.