Uncategorized

How to Lazy Load Images Without JavaScript Using the Loading Attribute

For years, developers relied on heavy JavaScript libraries like LazyLoad.js or lozad.js to defer offscreen images. Those days are over. With the native loading=”lazy” attribute, you can lazy load images without JavaScript in a single line of HTML. In this practical tutorial, we will show you exactly how it works, what browser support looks like in 2026, how to handle edge cases, and what kind of Lighthouse score boost you can realistically expect. Why Lazy Loading Images Matters Images are typically the heaviest assets on a web page. Loading every image upfront, even those far below the fold, wastes bandwidth, slows down Largest Contentful Paint (LCP), and hurts Core Web Vitals. Lazy loading defers offscreen images until the user scrolls near them, which results in: Faster initial page load Lower bandwidth consumption (huge win on mobile) Better Lighthouse Performance scores Improved SEO through better Core Web Vitals The One-Line Solution: loading=”lazy” Here is the entire implementation. No library, no script, no observer, no setup: <img src=”hero.jpg” alt=”Mountain landscape” loading=”lazy” width=”1200″ height=”800″> That is it. The browser handles everything natively. When the image is about to enter the viewport, the browser fetches it. When it is far away, the browser ignores it. The Three Possible Values Value Behavior lazy Defer loading until the image is near the viewport eager Load immediately (default behavior) auto Let the browser decide (not part of the spec, avoid) Browser Support in 2026 Native lazy loading is now supported by over 96% of global users. Every modern browser, including Chrome, Edge, Firefox, Safari (since version 15.4), Opera, and Samsung Internet, fully supports the attribute on both <img> and <iframe> elements. Browser Supported Since Chrome 76 Edge 79 Firefox 75 Safari 15.4 Opera 63 Browsers that do not understand the attribute simply ignore it and load the image normally. There is no breakage risk. Step-by-Step Tutorial Step 1: Identify Images to Lazy Load Apply loading=”lazy” only to images that appear below the fold. For your hero image and any image visible on first paint, use loading=”eager” or omit the attribute entirely. Step 2: Always Set width and height This is the most overlooked rule. Without explicit dimensions, the browser cannot reserve space for the image, which causes Cumulative Layout Shift (CLS). Always include them: <img src=”product.webp” alt=”Blue running shoe” loading=”lazy” width=”600″ height=”400″> Step 3: Combine with Modern Formats and srcset Native lazy loading works perfectly with responsive images: <img src=”photo-800.webp” srcset=”photo-400.webp 400w, photo-800.webp 800w, photo-1600.webp 1600w” sizes=”(max-width: 600px) 400px, 800px” alt=”Sunset over the ocean” loading=”lazy” width=”800″ height=”533″> Step 4: Lazy Load Iframes Too The same attribute works on iframes, which is perfect for embedded YouTube videos or maps: <iframe src=”https://www.youtube.com/embed/xyz” loading=”lazy” width=”560″ height=”315″></iframe> Fallback Strategy for Legacy Browsers If you still need to support a niche set of older browsers (Internet Explorer, very old Safari versions), you can apply a progressive enhancement pattern: Use loading=”lazy” as the primary mechanism Detect support via feature detection Optionally fall back to a tiny IntersectionObserver polyfill if (‘loading’ in HTMLImageElement.prototype) { // Native lazy loading is supported, do nothing } else { // Load a tiny fallback library only when needed const script = document.createElement(‘script’); script.src = ‘/js/lazysizes.min.js’; document.body.appendChild(script); } For 95% of projects in 2026, this fallback is unnecessary. The native attribute alone is enough. Real Performance Gains Measured with Lighthouse We tested a typical content-heavy blog page with 28 images on a simulated mobile 4G connection. Here are the results before and after applying loading=”lazy”: Metric Before After Improvement Performance Score 62 94 +32 points LCP 3.8s 1.9s -50% Total Bytes Transferred 4.2 MB 1.1 MB -74% Time to Interactive 5.6s 2.4s -57% These are real, repeatable gains achieved with a single HTML attribute. No JavaScript bundle. No build step. Common Mistakes to Avoid Lazy loading the hero image. This delays LCP and hurts your score. The first visible image should always be eager. Forgetting width and height. Causes layout shift and ruins CLS metrics. Applying it to background images. The attribute only works on <img> and <iframe>. CSS background images need a different approach. Combining with custom JS lazy loaders. Pick one. Running both creates conflicts and hurts performance. What About CSS Background Images? The native attribute does not cover CSS backgrounds. If you need that behavior, the cleanest workaround is to convert decorative backgrounds into <img> elements positioned with CSS, or use the content-visibility: auto CSS property to defer offscreen rendering. .section { content-visibility: auto; contain-intrinsic-size: 800px 600px; } FAQ Is loading=”lazy” safe to use in production? Yes. With over 96% browser support and graceful degradation in unsupported browsers, it is production-ready and recommended by Google, MDN, and web.dev. Does it hurt SEO? No. Googlebot fully supports native lazy loading and renders pages with it correctly. Just make sure your images have proper alt attributes and dimensions. Should I use it on every image? No. Skip it for images visible on first paint, especially the LCP image. Apply it only to offscreen content. Can I still use a JavaScript library on top? You can, but you should not. The native attribute is faster, lighter, and integrates with the browser’s prioritization engine. Mixing both adds complexity without benefit. Does it work with picture and source elements? Yes. Place loading=”lazy” on the inner <img> tag inside a <picture> element. It will apply regardless of which <source> the browser selects. Conclusion Native lazy loading is one of the highest-impact, lowest-effort performance wins available to web developers today. By replacing JavaScript-based solutions with the simple loading=”lazy” attribute, you reduce bundle size, improve Core Web Vitals, and give users a faster experience. If you have not migrated yet, this is the easiest performance optimization you will do this quarter. At GeminiWeb, we apply native-first techniques like this across every project to deliver fast, accessible, and future-proof websites. Got a slow site? Let us audit it.

How to Lazy Load Images Without JavaScript Using the Loading Attribute Read More »

How to Fix CORS Error in Express.js: Causes and Solutions

How to Fix CORS Error in Express.js: Causes, Solutions & Code Examples If you have ever built an API with Express.js and tried to call it from a frontend running on a different domain or port, you have almost certainly run into the dreaded CORS error. The browser console flashes a red message like “Access to fetch at … has been blocked by CORS policy” and your request silently fails. The good news: CORS errors are predictable and fixable once you understand what is happening behind the scenes. In this guide published by the GeminiWeb team, we will walk you through every common cause of CORS issues in Express.js and give you clear, copy-paste code examples for each fix. Whether you are dealing with simple requests, preflight failures, credentials, or multiple allowed origins, this post has you covered. What Is CORS and Why Does the Error Happen? CORS stands for Cross-Origin Resource Sharing. It is a security mechanism enforced by web browsers. When your frontend (e.g., https://app.example.com) makes an HTTP request to a backend on a different origin (e.g., https://api.example.com), the browser checks specific HTTP response headers to decide whether the frontend is allowed to read the response. If those headers are missing or misconfigured on your Express.js server, the browser blocks the response and throws a CORS error. Important: the server still processes the request. CORS is a browser-side enforcement, not a server-side block. Key CORS Response Headers Header Purpose Access-Control-Allow-Origin Specifies which origin(s) can access the resource Access-Control-Allow-Methods Lists the HTTP methods (GET, POST, PUT, DELETE, etc.) allowed Access-Control-Allow-Headers Lists the custom headers the client is permitted to send Access-Control-Allow-Credentials Indicates whether cookies/auth headers are allowed Access-Control-Max-Age How long (in seconds) the browser can cache the preflight response Most Common Causes of CORS Errors in Express.js Before jumping to solutions, let’s identify the root causes. In our experience at GeminiWeb working on dozens of Node.js projects, these are the issues we see most often: No CORS headers set at all on the Express server. Misconfigured Access-Control-Allow-Origin (wrong value, missing protocol, or typo). Preflight (OPTIONS) requests not handled, causing PUT/PATCH/DELETE or custom-header requests to fail. Credentials mode enabled on the client but the server uses a wildcard (*) origin. Multiple or dynamic origins not supported by the configuration. Middleware order issues where CORS headers are set after the route handler runs or after an error is thrown. Reverse proxy or hosting platform stripping headers before they reach the browser. Let’s fix each one. Solution 1: Use the cors npm Package (Quickest Fix) The fastest way to fix CORS errors in Express.js is to install the official cors middleware package. Step-by-step Install the package: npm install cors Import and use it in your Express app: const express = require(‘express’); const cors = require(‘cors’); const app = express(); // Enable CORS for all origins app.use(cors()); app.get(‘/api/data’, (req, res) => { res.json({ message: ‘CORS is working!’ }); }); app.listen(3000, () => { console.log(‘Server running on port 3000’); }); This adds Access-Control-Allow-Origin: * to every response. It is great for development, but not recommended for production because it allows any website to read your API responses. Solution 2: Allow a Specific Origin For production, you should restrict access to trusted domains only. app.use(cors({ origin: ‘https://myapp.example.com’ })); Now only requests originating from https://myapp.example.com will receive the proper CORS headers. Requests from any other origin will be blocked by the browser. Common mistake: forgetting to include the protocol. myapp.example.com without https:// will not match and the error will persist. Solution 3: Allow Multiple Origins If your API serves several frontends (e.g., a marketing site and a dashboard), you need to dynamically set the origin header. const allowedOrigins = [ ‘https://myapp.example.com’, ‘https://dashboard.example.com’, ‘http://localhost:5173’ ]; app.use(cors({ origin: function (origin, callback) { // Allow requests with no origin (e.g., mobile apps, curl) if (!origin) return callback(null, true); if (allowedOrigins.includes(origin)) { return callback(null, true); } else { return callback(new Error(‘Not allowed by CORS’)); } } })); Pro tip: store your allowed origins in an environment variable so you can change them without redeploying. # .env CORS_ORIGINS=https://myapp.example.com,https://dashboard.example.com const allowedOrigins = process.env.CORS_ORIGINS.split(‘,’); Solution 4: Handle Preflight Requests Properly What is a preflight request? Before sending certain requests (e.g., PUT, PATCH, DELETE, or any request with custom headers like Authorization), the browser sends a preliminary OPTIONS request called a preflight. If your server does not respond to this OPTIONS request with the correct headers, the actual request never fires. Fix with the cors package If you are using app.use(cors()) before your routes, preflight is handled automatically. But if you applied CORS only to specific routes, you also need to handle OPTIONS: // Enable preflight for all routes app.options(‘*’, cors()); // Then apply cors to your specific route app.get(‘/api/data’, cors(), (req, res) => { res.json({ message: ‘Hello’ }); }); Fix without the cors package (manual headers) app.use((req, res, next) => { res.header(‘Access-Control-Allow-Origin’, ‘https://myapp.example.com’); res.header(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’); res.header(‘Access-Control-Allow-Headers’, ‘Content-Type, Authorization’); // Handle preflight if (req.method === ‘OPTIONS’) { return res.sendStatus(204); } next(); }); Returning a 204 No Content status for OPTIONS tells the browser “go ahead, the actual request is allowed.” Solution 5: Fix CORS When Using Credentials (Cookies / Auth Headers) If your frontend sends cookies or an Authorization header, you need two things: The client must set credentials: ‘include’ (Fetch API) or withCredentials: true (Axios). The server must set Access-Control-Allow-Credentials: true and must not use a wildcard * for the origin. Client-side (Axios example) axios.get(‘https://api.example.com/user’, { withCredentials: true }); Server-side app.use(cors({ origin: ‘https://myapp.example.com’, credentials: true })); If you use origin: ‘*’ together with credentials: true, the browser will reject the response. This is one of the most frequent mistakes we see. Solution 6: Expose Custom Response Headers By default, the browser only exposes a limited set of response headers to JavaScript. If your API returns a custom header (like X-Total-Count for pagination), you need to explicitly expose it: app.use(cors({ origin: ‘https://myapp.example.com’, exposedHeaders: [‘X-Total-Count’, ‘X-Request-Id’] })); Solution 7: Fix Middleware Order Issues Express processes middleware in the order it

How to Fix CORS Error in Express.js: Causes and Solutions Read More »

How to Fix Bad Kerning in Logos: A Step-by-Step Guide

Why Kerning Can Make or Break Your Logo You have spent hours choosing the perfect typeface for a logo. The colors are right, the concept is strong, and the overall layout looks great. But something still feels off. The letters look awkward, cramped in some places and too loose in others. The problem? Bad kerning. Kerning is the adjustment of horizontal spacing between two individual letters. It is one of the most overlooked details in logo design, yet it is one of the most important. Poor kerning makes a logo look amateurish. Proper kerning makes it look polished, balanced, and unmistakably professional. In this guide, we will walk you through exactly how to kern a logo step by step. You will learn how to spot common kerning problems, fix them in popular design software, and develop an eye for letter spacing that separates amateur work from expert-level design. What Is Kerning, Exactly? Before we dive into the practical steps, let us make sure the definition is crystal clear. Kerning is the process of adjusting the space between two specific letters in a word. It is not the same as tracking (which adjusts spacing uniformly across an entire word or block of text) or leading (which controls vertical line spacing). Every letter has a unique shape. Some letters, like “A” and “V,” naturally create awkward gaps when placed next to each other. Others, like “H” and “I,” tend to sit more evenly. Kerning addresses those uneven gaps on a pair-by-pair basis. Kerning vs. Tracking vs. Letter Spacing: A Quick Comparison Term What It Adjusts When to Use It Kerning Space between two specific letters Logo design, headlines, display type Tracking Uniform spacing across a whole word or line Body text, stylistic all-caps treatments Letter Spacing (CSS) Similar to tracking, applied in web/code Web design, CSS styling For logo work, manual kerning is essential. Auto-kerning settings in design software get you part of the way there, but they almost never produce perfect results for display-size typography like logos. How to Spot Bad Kerning in a Logo The first step in learning how to kern a logo is training your eye to recognize problems. Here are the most common signs of poor kerning: Uneven “rivers” of space: Some letter pairs have wide gaps while others are tightly packed. Letters that appear to touch or collide: Certain combinations look like they are merging into one shape. Words that read as two separate words: A large gap in the middle of a word can split it visually. An overall “wobbly” feeling: The word does not look balanced even though you cannot immediately pinpoint why. Problematic Letter Combinations to Watch For Some letter pairings are notorious for causing kerning headaches. Keep a close eye on these combinations: AV, AW, AT, AY – The diagonal and horizontal shapes create large triangular gaps. RA, PA, FA – The arm or crossbar of the first letter creates space above the “A.” To, Tr, Ta – The overhang of the “T” leaves excessive room next to lowercase letters. LT, LY, LA – The open right side of “L” creates visible holes. WA, VA, Yo – Diagonal strokes paired with round or angled letters. ry, ly, ty – Lowercase combinations that often need tightening. If your logo contains any of these pairs, you will almost certainly need to adjust the kerning manually. How to Kern a Logo: Step-by-Step Process Now let us get into the practical workflow. This process works regardless of which design tool you use, whether that is Adobe Illustrator, Figma, Affinity Designer, or another application. Step 1: Type Out Your Logo Text and Choose Your Font Start by setting your logo text in the typeface you have selected. Use a large point size so that spacing issues are easier to see. Working at 150pt or larger on screen is a good starting point. At this stage, leave the kerning on the default “Auto” or “Metrics” setting. This gives you the font designer’s built-in kerning as your baseline. Step 2: Switch to Optical Kerning (Optional Starting Point) Most professional design tools offer an “Optical” kerning mode that calculates spacing based on the actual shapes of the letters rather than the font’s built-in kerning table. Try both Metrics and Optical settings and see which one gives you a better starting point. For many display fonts, Optical kerning produces a more even result. But neither setting will be perfect for logo work, so manual adjustment is always the next step. Step 3: Kern in Groups of Three Letters This is one of the most effective techniques professional typographers use. Instead of trying to evaluate an entire word at once, break it down into groups of three consecutive letters. Here is how it works: Look at the first three letters of your logo text. Focus only on those three. Adjust the spacing between the first and second letter until the gap looks visually equal to the gap between the second and third letter. Move forward by one letter. Now look at letters 2, 3, and 4. Repeat the process. Continue until you reach the end of the word. Go back to the beginning and repeat the entire process one or two more times. The goal is not to make every gap exactly the same number of pixels. The goal is to make every gap feel visually equal. Because letters have different shapes (round, straight, diagonal, open), the actual measured distances will vary. What matters is the perceived balance of space. Step 4: Use the Squint Test Once you have completed your initial kerning pass, squint your eyes or step back from your screen. When the letters blur slightly, uneven spacing becomes much more obvious. You will see dark clumps where letters are too tight and bright holes where they are too loose. This simple technique is used by professional designers every day. It works because squinting removes your ability to read the actual letters, forcing your brain to evaluate the rhythm

How to Fix Bad Kerning in Logos: A Step-by-Step Guide Read More »

Company Name

Gemini Web

Company Address

3444 Hall Valley Drive, Davy, WV 24828 USA

Company Email

[email protected]

Copyright © 2022 Gemini Web. All Rights Reserved.