Two developers build the same page. Same images, same form, same content. One loads faster, scores better on Core Web Vitals, and feels smoother on mobile. The other does not.
The difference is four HTML attributes. Each one takes about thirty seconds to add. Each one has a real, measurable impact on the people using your page.
Here is what they do and how to use them right.
Here are the exact code examples:
loading
By default, the browser fetches every image it finds in your HTML immediately, even the ones three screens below the fold that the user may never reach. On pages with many images, that is a lot of wasted bandwidth upfront.
The loading attribute changes this.
<!-- Loads immediately -->
<img src="hero.jpg" alt="Hero" loading="eager">
<!-- Waits until the user scrolls near it -->
<img src="article-photo.jpg" alt="Photo" loading="lazy">
<!-- Works on iframes too -->
<iframe src="map.html" title="Map" loading="lazy"></iframe>
loading="lazy" defers the request until the user is actually close to that element. If they never scroll there, the request never happens.
One thing to be careful about. Never use loading="lazy" on images visible when the page first opens. Those images should load immediately. Deferring them increases your Largest Contentful Paint time, which affects both user experience and search rankings.
The rule is simple. Above the fold gets eager or nothing. Below the fold gets lazy.
<!-- Hero. Load it right away. -->
<img src="hero.jpg" alt="Hero" loading="eager" width="1200" height="600">
<!-- Further down the page. Let it wait. -->
<img src="section-photo.jpg" alt="Photo" loading="lazy" width="800" height="500">
Always include width and height with lazy loaded images. Without them, the layout shifts when the image arrives, which is jarring for users and hurts your Cumulative Layout Shift score.
fetchpriority
The browser assigns a priority to every resource it loads. Stylesheets are high priority because they block rendering. Images are low priority by default because most of them are below the fold.
The problem is your hero image is also low priority by default, even though it is the most important thing on the page.
fetchpriority lets you correct that.
<!-- Tell the browser this image matters more than other images -->
<img src="hero.jpg" alt="Hero" fetchpriority="high">
<!-- This script is not critical, let it wait -->
<script src="analytics.js" fetchpriority="low" defer></script>
<!-- Works on preloads too -->
<link rel="preload" href="hero.jpg" as="image" fetchpriority="high">
The most impactful use is on your LCP image. Adding fetchpriority="high" moves it to the front of the loading queue from the very start instead of sitting behind fonts and scripts that got there first.
<img src="hero.jpg" alt="Hero" fetchpriority="high" loading="eager" width="1200" height="600">
fetchpriority="high" and loading="eager" are doing different things here. eager means do not defer this image. fetchpriority="high" means when competing with other resources for bandwidth, this one goes first. Both belong on your hero image.
The low value is useful when you are preloading something that is not needed for the initial render and you do not want it competing with resources that are.
<link rel="preload" href="secondary-font.woff2" as="font" fetchpriority="low" crossorigin>
This attribute became fully supported across all major browsers in October 2024 and works as a hint, meaning the browser will respect it when it can and use its own judgment when needed.
autocomplete
The autocomplete attribute tells the browser what kind of data lives in a field. When the browser knows this, it can offer the right saved value instantly, whether that is a saved address, a credit card number, or login credentials.
Most developers know it accepts on and off. What is less known is that it accepts over 50 specific values, each one mapping to a particular type of information.
<!-- Personal details -->
<input type="text" autocomplete="given-name" placeholder="First name">
<input type="text" autocomplete="family-name" placeholder="Last name">
<input type="email" autocomplete="email" placeholder="Email"><input type="tel" autocomplete="tel" placeholder="Phone">
<!-- Address -->
<input type="text" autocomplete="street-address" placeholder="Street address">
<input type="text" autocomplete="postal-code" placeholder="Postal code">
<input type="text" autocomplete="country" placeholder="Country">
<!-- Payment -->
<input type="text" autocomplete="cc-name" placeholder="Name on card">
<input type="text" autocomplete="cc-number" placeholder="Card number">
<input type="text" autocomplete="cc-exp" placeholder="Expiry">
<input type="text" autocomplete="cc-csc" placeholder="CVV">
<!-- Login -->
<input type="text" autocomplete="username" placeholder="Username">
<input type="password" autocomplete="current-password" placeholder="Password">
<input type="password" autocomplete="new-password" placeholder="Create a password">
current-password and new-password are worth understanding separately. current-password tells the browser the user is entering an existing password, so it offers saved credentials. new-password tells it the user is creating one, so it offers to generate a strong password instead. One value, completely different behavior.
For one-time verification codes there is a dedicated value:
<input type="text" autocomplete="one-time-code" inputmode="numeric" maxlength="6">
On iOS, when this is set and an SMS with a code arrives, the keyboard automatically surfaces a one-tap option to fill it in. That entire copy-and-paste flow collapses into a single tap because the browser understands what the field needs.
inputmode
When a user taps an input on mobile, a keyboard appears. Which keyboard appears is something you can control.
inputmode tells the browser which virtual keyboard to show. It does not change validation or field behavior, only the keyboard. This makes it more flexible than changing the type attribute, which affects both.
<!-- Digits only, without type="number" side effects -->
<input type="text" inputmode="numeric">
<!-- Digits plus decimal point, right for prices -->
<input type="text" inputmode="decimal">
<!-- Telephone keypad with * and # -->
<input type="text" inputmode="tel">
<!-- Email keyboard with @ shortcut -->
<input type="email" inputmode="email">
<!-- URL keyboard with / and .com -->
<input type="text" inputmode="url">
<!-- Search keyboard, enter key labeled Search --><input type="search" inputmode="search">
numeric and decimal are the ones people mix up most often. numeric shows digits 0 to 9 only. decimal adds a decimal separator. Use decimal for prices and measurements, numeric for PINs, postal codes, and verification codes.
Here is what a complete credit card field looks like when autocomplete and inputmode work together:
<input type="text" autocomplete="cc-number" inputmode="numeric" pattern="[0-9\s]{13,19}" placeholder="1234 5678 9012 3456">
Each attribute has a specific job. autocomplete tells the browser what the field collects. inputmode shows the right keyboard. pattern handles validation. None of them overlap.
The same approach applies to an OTP field:
<input type="text" autocomplete="one-time-code" inputmode="numeric" maxlength="6" pattern="\d{6}">
On iOS this combination triggers automatic SMS code detection. A flow that used to require switching apps and copying a code becomes one tap. The attributes are doing all the work.
What connects all four
None of them need JavaScript. None of them change how your page looks. They are pieces of information you give the browser so it can make better decisions for your users.
loading controls when a resource is fetched. fetchpriority controls how urgently it is fetched. autocomplete tells the browser what data a field expects. inputmode tells it which keyboard fits that data.
Small additions. Real impact.
The WHATWG Living Standard lists every valid *`*autocomplete`* value. If you build forms regularly, the full list is worth a look.
*
Did you learn something good today as a developer?
Then show some love.
© Muhammad Usman
WordPress Developer | Website Strategist | SEO Specialist
Don’t forget to subscribe to Developer’s Journey to show your support.







Top comments (3)
This is one of those posts where you realize how many small things we ignore daily.
I have used most of these attributes, but seeing them explained together like this makes it click even more how much control the browser already gives us if we just guide it properly.
Simple stuff, but it really improves UX in a quiet way.
User experience is the first and final thing we all should worry about.
Let's make the internet less boring than it already is.