Images
The img() and picture() helpers generate image markup that prevents CLS by requiring dimensions and handles loading priority correctly. Pulse targets 0.00 CLS — these helpers enforce the attributes that make that possible.
Importing
// In your page spec or component
import { img, picture } from '@invisibleloop/pulse/image'
img(options)
Generates an optimised <img> element:
img({
src: '/images/hero.jpg',
alt: 'A hero image showing our product',
width: 1200,
height: 630,
priority: true, // → eager loading + high fetchpriority
})
Output:
<"tok-fn">class="tok-kw">img "tok-fn">src="/images/hero.jpg" "tok-fn">alt="A hero image showing our product" "tok-fn">width="1200" "tok-fn">height="630" "tok-fn">loading="eager" "tok-fn">decoding="async" "tok-fn">fetchpriority="high">
img() options
| Option | Type | Required | Description |
|---|---|---|---|
src | string | Yes | Image URL. |
alt | string | Yes | Alt text. Required for accessibility. Use an empty string for decorative images. |
width | number | Recommended | Intrinsic width in pixels. Prevents CLS by reserving layout space. |
height | number | Recommended | Intrinsic height in pixels. Prevents CLS. |
priority | boolean | No | If true: loading="eager" + fetchpriority="high". Use for LCP images. Default: false. |
class | string | No | CSS class applied to the <img> element. |
width and height are required to prevent Cumulative Layout Shift. Without them the browser cannot reserve layout space before the image loads. Pulse targets 0.00 CLS — omitting these attributes breaks that guarantee.picture(options)
Generates a <picture> element with modern format sources and a fallback <img>. Use this to serve AVIF or WebP to browsers that support them, with JPEG/PNG as the fallback:
picture({
src: '/images/hero.jpg', // fallback
alt: 'Hero image',
width: 1200,
height: 630,
priority: true,
sources: [
{ src: '/images/hero.avif', type: 'image/avif' },
{ src: '/images/hero.webp', type: 'image/webp' },
],
})
Output:
<"tok-fn">class="tok-kw">picture>
<"tok-fn">class="tok-kw">source "tok-fn">srcset="/images/hero.avif" "tok-fn">type="image/avif">
<"tok-fn">class="tok-kw">source "tok-fn">srcset="/images/hero.webp" "tok-fn">type="image/webp">
<"tok-fn">class="tok-kw">img "tok-fn">src="/images/hero.jpg" "tok-fn">alt="Hero image" "tok-fn">width="1200" "tok-fn">height="630" "tok-fn">loading="eager" "tok-fn">decoding="async" "tok-fn">fetchpriority="high">
</"tok-fn">class="tok-kw">picture>
picture() options
| Option | Type | Description |
|---|---|---|
src | string | Fallback image URL (JPEG/PNG for universal compatibility). |
alt | string | Alt text — shared by the inner <img>. |
width | number | Intrinsic width — applied to inner <img>. |
height | number | Intrinsic height — applied to inner <img>. |
priority | boolean | If true: eager loading + high priority. |
class | string | CSS class on the inner <img>. |
sources | {src, type}[] | Modern format sources in preference order (AVIF first, WebP second). |
When to use priority
Set priority: true on the Largest Contentful Paint (LCP) element — typically the hero image above the fold. This tells the browser to load it with high priority, improving LCP.
Every other image should omit priority (defaults to lazy loading with loading="lazy"). Lazy images are not fetched until they approach the viewport — reducing initial page weight and speeding up load time.
priority: true. Using it on multiple images defeats the purpose — every image becomes "high priority" which is the same as no image being prioritised.Using in a view
import { img, picture } from '@invisibleloop/pulse/image'
export default {
route: '/blog/:slug',
state: {},
server: {
data: async (ctx) => ({ post: await db.posts.findBySlug(ctx.params.slug) }),
},
view: (state, server) => `
<article>
${picture({
src: server.post.heroImage,
alt: server.post.heroAlt,
width: 1200,
height: 630,
priority: true,
sources: [
{ src: server.post.heroImage.replace('.jpg', '.avif'), type: 'image/avif' },
{ src: server.post.heroImage.replace('.jpg', '.webp'), type: 'image/webp' },
],
})}
<h1>${server.post.title}</h1>
</article>
`,
}