
Nextjs API Components
Every React framework eventually ends up shipping its own set of "special" components — the ones that aren't quite ordinary JSX, because they're doing something the framework specifically knows how to optimize. Next.js has five of these: Font, Form, Image, Link, and Script. If you've spent any time in an App Router codebase, you've almost certainly reached for next/image or next/link without thinking twice about why they exist instead of a plain <img> or <a>.
This article is the map, not the territory. Each of these five components has its own dedicated, much longer write-up elsewhere on this blog covering its full API surface, edge cases, and gotchas. What's worth doing here is stepping back and asking a more useful question: what actually earns a component the label "built-in" in Next.js, and why does it matter that these particular five got that treatment instead of being left to userland libraries or native HTML?
What Makes a Component "Built-In"
A built-in Next.js component isn't just a convenience wrapper. In every one of these five cases, the component is solving a problem that genuinely can't be solved well with a plain HTML tag alone, because the fix requires cooperation from the build pipeline, the server, or the runtime — not just clever prop-passing in the browser.
Consider what a plain <img src="/hero.png" /> actually does: it tells the browser to fetch that exact file, at its exact dimensions, with no resizing, no format negotiation, and no lazy-loading unless you hand-roll it. The browser has no idea that a WebP version half the size would look identical, or that this image is 800px below the fold and doesn't need to block anything. next/image exists because that information — "this image can be resized, reformatted, and deferred" — has to be known and acted on somewhere between build time and request time, and a static <img> tag has no mechanism for that conversation to happen.
The same pattern shows up in every one of the other four:
- Font needs to intercept a
@font-facedeclaration at build time, self-host the actual font files, and injectpreloadandsize-adjusthints before the browser ever asks for a font — none of which a<link>tag to Google Fonts can do without shipping a render-blocking external request. - Form needs to know it's inside an App Router route so it can prefetch the destination page in the background and update the URL client-side after a plain HTML
<form>submission, instead of forcing a full-page reload. - Image needs a request-time or build-time image optimization service sitting behind it, resizing and re-encoding images on demand — logic a static tag simply has nowhere to live.
- Link needs to know about the App Router's route tree well enough to prefetch a destination route's code and data before the user even clicks, something a plain
<a>has no concept of. - Script needs to know about the page lifecycle — hydration, interactivity, idle time — well enough to schedule a third-party
<script>tag at exactly the right moment instead of blindly wherever it's placed in the JSX tree.
Every single one of these requires the framework itself to be involved, either at build time (bundling, static analysis) or at runtime (the router, the server). That's the real dividing line between "built-in component" and "component someone could write themselves in userland": it needs privileged access to something React alone doesn't expose.
Why Not Just Use a Third-Party Library?
For every one of these five, a userland equivalent already existed before Next.js shipped its own version — react-helmet for fonts and head tags, react-lazy-load-image-component for images, react-router's Link, dozens of "load this script async" utility hooks. So why does Next.js bother maintaining its own?
The honest answer is that a third-party library, by definition, sits outside the framework's build pipeline and server runtime. It can approximate what these built-ins do, but it's always working with less information. A third-party lazy-loading image library doesn't know your next.config.js image domains allowlist. A third-party font loader doesn't know which routes will be statically generated and which won't, so it can't guarantee the font is available before first paint. A third-party router link doesn't have access to the App Router's internal route manifest, so its "prefetching" is really just a fetch call it's guessing about, not a genuine integration with the framework's data-fetching layer.
None of this makes third-party alternatives useless — plenty of teams still reach for a CMS-specific image component or a custom router abstraction for good reasons specific to their stack. But it does mean that, for the common case, the built-in components will consistently outperform a generic equivalent, because they're not guessing at the framework's internals from the outside — they are part of the framework's internals.
A Fast Comparison of What Each One Solves
If you're deciding which of the five deep-dive articles to read first, here's the one-sentence version of each:
Font (next/font) — solves layout shift and slow font loading by self-hosting Google Fonts or local font files and computing fallback metrics automatically, so text doesn't visibly jump when the real font finishes loading.
Form (next/form) — solves the "full page reload on every search bar submit" problem by making a plain HTML form behave like a client-side navigation, complete with prefetching of the target page.
Image (next/image) — solves oversized, unoptimized images by automatically resizing, reformatting, and lazy-loading images, plus reserving layout space to prevent shift while the image loads.
Link (next/link) — solves slow perceived navigation by prefetching a route's JavaScript and data ahead of a click, so the actual click feels instantaneous.
Script (next/script) — solves third-party scripts blocking the main thread by giving you explicit control over when a script loads relative to the page becoming interactive.
A Practical Note the Docs Don't Spell Out: These Compose
One thing that's easy to miss when you're learning these one at a time is that they're designed to be used together, not in isolation. A typical marketing page in a real App Router project might use all five in the same layout: Font in the root layout for typography, Image for the hero and product photos, Link for internal navigation, Form for a newsletter signup, and Script for the analytics tag at the bottom of the page. None of them conflict with each other, and none of them require you to opt out of the others to use one.
The other practical detail worth knowing before you dive into the individual articles: every one of these five is a drop-in replacement for something you already know how to use. next/image still takes a src and produces an <img> under the hood. next/link still renders an <a>. You're not learning a new mental model for each one — you're learning what extra props each one adds on top of the HTML element you already understand, and why the framework needed to get involved to make those props actually do something.
Key Takeaways
| Component | HTML equivalent | Problem it solves |
|---|---|---|
next/font | <link rel="stylesheet"> / @font-face | Layout shift and slow web font loading |
next/form (<Form>) | <form> | Full-page reloads on simple form submissions |
next/image (<Image>) | <img> | Unoptimized image size, format, and loading |
next/link (<Link>) | <a> | Slow perceived client-side navigation |
next/script (<Script>) | <script> | Third-party scripts blocking the main thread |
These five components exist because Next.js can see things a plain HTML tag can't: the build pipeline, the route tree, the render lifecycle. If you take one thing from this overview before reading the dedicated articles, let it be this — reach for the built-in version by default, and only step outside it when you have a specific, concrete reason a third-party or hand-rolled equivalent needs to do something these five genuinely can't.


