Next.js pre-build script for detecting missing images before deployment

Catch Missing Images Before Deploy: A Simple Pre-Build Script for Next.js

Software Development
August 06, 2026
6-10 min

Share blog

Introduction

"At a glance: a dependency-free Node script validates every local /images/... reference before next build, catching missing files, unsafe names, and case mismatches before deploy."

Image failures are deceptively expensive. A page can look perfect on a developer's machine, pass review, and then ship with blank logos, missing product screenshots, or broken hero images. At Omax Tech, we recently added a small pre-build validation step to stop that class of issue before deployment.

Our stack is Next.js 15 on Vercel. We currently use unoptimized: true in next.config.ts, so image assets are served through direct /images/... URLs rather than the Vercel Image Optimizer. That makes asset paths straightforward but it also means every referenced file and its exact casing must be correct.

The result is simple: before every build, a Node.js script validates our image library and references. If it finds an issue, the build stops. The same check runs locally and in Vercel CI, so a broken image cannot quietly reach production.

The problem: it worked locally, then broke in production

Most of the failures came down to differences between local Windows development and Vercel’s production environment.

Windows file systems are commonly case-insensitive. A component can request /images/technology/wordpress.webp while the actual asset is named WordPress.webp, and it may still appear locally. Vercel treats those as different files.

We also found other easy-to-miss issues:

  • Paths referencing files that had been renamed or removed
  • An old .jpg reference after the asset was converted to webp
  • Fallback image paths left behind in components
  • Filenames containing spaces, ampersands, quotes, parentheses, commas, or an en dash
  • Path casing that did not exactly match the file on disk

Each problem is small in isolation. Across a large site, they are difficult to spot through manual testing alone.

Why development misses it

Local development is a useful but imperfect production simulation. In this case, Windows can mask casing errors that may fail in Vercel’s production environment. A route may also be missed during a visual QA pass, especially when a problematic image is in a rarely used component, a fallback state, or a static page.

Relying on the browser to reveal missing assets is late feedback. We wanted a deterministic check that answers two questions before the build begins: does every local image reference resolve, and will it resolve with the exact same name on a case-sensitive file system?

The solution: validate images before next build

We added a dependency-free Node ESM script at scripts/validate-images.mjs. It scans public/images/, then searches image references under src/ across .tsx, .ts, and .json files.

The script reports grouped errors instead of making developers hunt through a long build log:

  • BAD_FILENAME: /public/images/team/John & Jane.webp - the filename contains an ampersand (&), which is not allowed by the validation rule.
  • MISSING FILE: /images/technology/angular.svg - referenced in code, but no matching file exists in public/images/technology/.
  • CASE_MISMATCH: /images/technology/wordpress.webp - the file exists as /images/technology/WordPress.webp, but the casing does not match exactly.
  • BAD_REF: /images/blog/my image.webp - the image path written in code contains a space, which is considered an unsafe character.

The full production script is available in our repository. The version below shows the core approach in a compact form; the complete implementation is included with this post’s deliverables.

javascript
1const files = await walk('public/images');
2const assetPaths = new Set(files.map(toPublicPath));
3const folded = new Map(files.map((file) => [toPublicPath(file).toLowerCase(), toPublicPath(file)]));
4
5for (const sourceFile of await walk('src')) {
6 if (!/\.(tsx?|json)$/.test(sourceFile)) continue;
7 for (const imagePath of extractImagePaths(await readFile(sourceFile, 'utf8'))) {
8 if (hasInvalidCharacters(imagePath)) report('BAD_REF', imagePath, sourceFile);
9 else if (!assetPaths.has(imagePath)) {
10 report(folded.has(imagePath.toLowerCase()) ? 'CASE_MISMATCH' : 'MISSING FILE', imagePath, sourceFile);
11 }
12 }
13}
14
15if (errors.length) process.exit(1);

Quick setup

Add a manual command and a prebuild hook to package.json:

json
1{
2 "scripts": {
3 "validate:images": "node scripts/validate-images.mjs",
4 "prebuild": "npm run validate:images",
5 "build": "next build"
6 }
7}

Then keep the validator at scripts/validate-images.mjs and commit it with the project.

Use these commands during development:

bash
npm run validate:images # Run the image check at any time
npm run build # Validate first, then run next build

Because npm automatically runs prebuild before build, no separate CI command is required. Vercel runs the normal build command, which means image validation runs there too.

Works with Vercel, Netlify, and other CI platforms

This validation script is not specific to Vercel. It runs wherever your Next.js project runs npm run build, including Netlify, Cloudflare Pages, GitHub Actions, GitLab CI, AWS Amplify, Docker-based pipelines, and self-hosted environments.

Let's Build Something Great Together

Ready to transform your idea into a powerful software solution? Talk to our experts and get a free consultation.

Contact Us

We use Vercel for this project, so it is referenced throughout this article. Because the validation runs through the npm prebuild hook, it automatically runs before every production build on Vercel and any other Node.js-based hosting platform. If an image path, filename, or casing issue is found, the build stops before deployment.

What developers see

When an asset is missing, the failure is direct and actionable:

bash
IMAGE VALIDATION FAILED (55 errors)
[MISSING FILE]
/images/technology/angular.svg
Referenced in src/components/technology-stack.tsx — file not found

At this point the build stops, so the issue is fixed while the source file and intended asset are still clear.

After correcting references, filenames, extensions, and casing, the success path is intentionally quiet:

bash
Image validation passed (1725 files under public/images, 928 references checked)

Next.js then proceeds with the build normally. In our project, the build completed successfully without any issues.

Terminal output showing a successful image validation running before the Next.js production build
Figure 1. Failed pre-build image validation reports missing local assets and stops the build.
Terminal output showing a failed pre-build image validation that lists missing local assets and stops the build
Figure 2. Successful image validation runs automatically before the Next.js production build.

Refinement: validate real image references, not plain text

We started with a simple rule: flag every /images/... path found in source files. It caught real problems, but it also had a blind spot in the other direction. A path mentioned in prose, such as a blog post describing what a missing-file error looks like, was treated the same as an asset the page actually loads. That produced occasional false 'missing file' reports for images that were never rendered.

We refined the scan so a path is validated only when it appears as an actual image reference in code, such as src="/images/...", icon: '...', or url('...'). If the code does not load the image, the image is not treated as a dependency, and a mention in plain text is ignored.

This article is a convenient test case. It references /images/technology/wordpress.webp and /images/technology/angular.svg in paragraphs and in the example output above. With the refined rule, npm run validate:images passes without complaint, because those are prose mentions, not references. Write the same path as a real src in a component while the file is missing, and the build stops as before.

The refinement changes the signal, not the safety net. Missing files, exact casing mismatches, and unsafe filenames are still caught wherever they are genuinely referenced. What changed is the noise: a build no longer fails over an image path that only appears in text.

Our results

The first run found 55 validation errors. They included missing files, incorrect .jpg versus .webp extensions, stale fallback paths in components, and three case mismatches. None required a complex fix-but finding all of them manually would have taken far longer.

Once corrected, the script validated 1,725 files under public/images against 928 references in source code. The build passed and generated 151 static pages. More importantly, the check now runs wherever the application builds, including Vercel.

Why this is worth keeping

This small guardrail gives both frontend developers and team leads clearer signals:

  • It turns a production-only image failure into a local, source-linked error.
  • It enforces a predictable asset naming convention as the library grows.
  • It makes production filesystem compatibility part of the normal development workflow.
  • It catches regressions in CI without adding a package or a separate service.

This production-first approach is part of how we deliver reliable, scalable digital products through our web application development services

FAQ

Does this replace manual testing?

No. The validator confirms that local image paths exist and match case; it does not assess visual quality, layout, responsive behavior, or whether the chosen image is the right one. It complements visual QA.

Does it work on Vercel CI?

Yes. Vercel invokes the project build command. Since npm run build triggers prebuild, validation runs before next build there as well, and any errors fail the deployment.

What about external URLs?

This validator targets local /images/... assets in public/images. External URLs need a different policy, such as domain allowlists, runtime fallbacks, or a network-aware check. We intentionally do not treat them as local files.

Conclusion

A pre-build image check is a small addition with a high return. It catches errors at the point where they are cheapest to fix, protects deployments from environment-specific behavior, and gives the team one reliable standard for static assets.

  • Run deterministic asset validation before every production build.
  • Treat filename casing and characters as part of your deployment contract.
  • Keep visual testing, but let automation catch broken local paths first.

Blogs

Discover the latest insights and trends in technology with the Omax Tech Blog.

View All Blogs
Omax | Blog | How to Add LiveKit Video Calling to a Next.js App
12-14 min
September 11, 2026

How to Add LiveKit Video Calling to a Next.js App

Add embedded video & audio calling to Next.js with LiveKit Cloud. Compared vs Twilio, Daily, Agora, Zoom — plus token auth, guests & recording.

Read More
Omax | Blog | We chose ECS over EKS: what we gained and what we gave up
8-10 min
September 10, 2026

We chose ECS over EKS: what we gained and what we gave up

An honest comparison of ECS vs EKS the costs, tradeoffs, and real-world reasoning behind choosing ECS for a production platform on AWS.

Read More
Omax | Blog | Upgrading Legacy Systems: From Outdated Technology to Competitive Advantage
8-10 min
September 07, 2026

Upgrading Legacy Systems: From Outdated Technology to Competitive Advantage

Learn how to upgrade legacy systems through application modernization, API integration, cloud migration, security improvements, and incremental system upgrades without disrupting business operations.

Read More
Omax | Blog | Building Distributed Tracing and Observability with AWS X-Ray
12-14 min
September 04, 2026

Building Distributed Tracing and Observability with AWS X-Ray

A practical guide to correlating requests across a multi-tier application using correlation IDs, AWS X-Ray segments, and structured logging for faster incident debugging.

Read More
Omax | Blog | Designing Before and After AI: What Really Changed
6-7 min
September 03, 2026

Designing Before and After AI: What Really Changed

A look at how AI has transformed UI/UX design from manual wireframes and slow research to AI-assisted prototyping, design-to-code, and personalization at scale.

Read More
Omax | Blog | Beyond Prompting: Managing Context and Tokens in AI Coding Tools
12-14 min
September 03, 2026

Beyond Prompting: Managing Context and Tokens in AI Coding Tools

Ever wondered why your AI coding agent starts losing context or hits a hard limit mid-task? The answer lies in tokens and the context window. Good AI coding is not about giving the model the most information. It is about giving it the right information at the right time.

Read More
Omax | Blog | What Is llms.txt? How It Helps Google, AI Search, and Agentic Browsing Find Your Website
10-12 min
August 31, 2026

What Is llms.txt? How It Helps Google, AI Search, and Agentic Browsing Find Your Website

Learn what llms.txt is, how it differs from sitemap.xml and robots.txt, and how it can help your site get found by Google, AI search tools, and AI agents.

Read More
Omax | Blog | Build an Automated Image Compression Script with Sharp and SVGO
7-8 min
August 28, 2026

Build an Automated Image Compression Script with Sharp and SVGO

Compress images from the terminal with a Node.js script powered by Sharp and SVGO a safe, two-step workflow that keeps your site fast without bloating your repo.

Read More
Omax | Blog | The Right Way to Migrate from MySQL to AWS Aurora DSQL
7-8 min
August 25, 2026

The Right Way to Migrate from MySQL to AWS Aurora DSQL

Migrating a production database is one of the highest-risk changes you can make to an application. Moving from MySQL to AWS Aurora DSQL raises the stakes further...

Read More