#t
50 messages · Page 1 of 1 (latest)
Hi @haughty onyx. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
Yes, you can use Astro’s <Image /> and <Picture /> components with images in the public/ folder.
For <Image />, pass a string path relative to public/ and provide width and height (Astro cannot infer these for public/ images):
---
import { Image } from 'astro:assets';
---
<Image
src="/images/my-public-image.png"
alt="descriptive text"
width="200"
height="150"
/>
[Image src prop; width/height for public]
The docs also note that you can use <Image /> for public/ (and unconfigured remote) images even though they are not optimized; it mainly gives you consistent markup and CLS protection. [Astro components for images]
For <Picture />, the same src rules apply as for <Image />, so you can also point it at a public/ path, but the knowledge sources do not explicitly show a public/ example. They only state that src can be a string and that public/ images are never optimized or made responsive. [Picture component; Images in .astro files; Responsive images note]
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt would I be able to use fetchpriority attribute? What params are not available to me if I use astro's Picture and Image components for images in /public folder?
Let me check the knowledge base for you again. One moment! :books:
You can use fetchpriority, but the knowledge sources only document it indirectly via the priority prop on <Image /> / <Picture />, not as a manual attribute.
Using fetchpriority via priority
Astro provides a priority boolean prop on <Image /> and <Picture />. When set, it automatically adds:
loading="eager"
decoding="sync"
fetchpriority="high"
to the rendered <img> tag. [priority]
This works regardless of where the image file lives (including /public), because priority is a component prop, not tied to optimization. The docs do not explicitly restrict priority for public/ images.
What is different for /public images?
For images in public/ used with <Image /> / <Picture />, the documented limitations are:
- No optimization / transformation:
public/images are “never optimized” and responsive images are “not supported.” [Responsive behavior] - Must provide
widthandheight: Astro cannot analyzepublic/files, sowidthandheightare required. [width/height for public]
The docs do not list any component props that are forbidden for public/ images; they only state that optimization and responsive behavior don’t apply, and that dimensions must be set manually.
So:
- You can use
priority(and thus getfetchpriority="high"). - You still must set
widthandheightyourself. - Optimization-related behavior (e.g. responsive
layoutgenerating multiple sizes) will not apply topublic/images, even if you set those props. The docs do not detail exactly which props are ignored, only thatpublic/images are not optimized or made responsive.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt with Picture component, will I be able to specify different images for different resolutions and different crop 16x9, 4x3 and so on for different screen sizes?
Let me check the knowledge base for you again. One moment! :books:
The knowledge sources don’t show a way to do art direction (different source files / crops per breakpoint) directly with the built‑in <Picture /> props.
Astro’s <Picture />:
- Takes a single
srcimage and generates multiple formats and/or sizes from that one source. [Picture guide; Picture reference] - Accepts all
<Image />props (e.g.layout,widths,sizes,formats, etc.), but always based on that singlesrc. [Picture reference]
The docs do not describe any API on <Picture /> to pass multiple different src images (e.g. one 16:9, one 4:3) for different media queries.
For that use case, the official recipe shows building a custom component using getImage() and a manual <picture> element, where you can choose different images per media query:
---
import type { ImageMetadata } from "astro";
import { getImage } from "astro:assets";
interface Props {
mobileImgUrl: string | ImageMetadata;
desktopImgUrl: string | ImageMetadata;
alt: string;
}
const { mobileImgUrl, desktopImgUrl, alt } = Astro.props;
const mobileImg = await getImage({
src: mobileImgUrl,
format: "webp",
width: 200,
height: 200,
});
const desktopImg = await getImage({
src: desktopImgUrl,
format: "webp",
width: 800,
height: 200,
});
---
<picture>
<source media="(max-width: 799px)" srcset={mobileImg.src} />
<source media="(min-width: 800px)" srcset={desktopImg.src} />
<img src={desktopImg.src} alt={alt} />
</picture>
So, based on the docs:
<Picture />alone: no documented support for multiple different source images / crops per breakpoint.- For different crops (16:9, 4:3, etc.) at different resolutions: use a custom
<picture>withgetImage()as in the recipe.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt If I am using images with Picture component, does Responsive image behavior works? Can I use layout, Responsive image styles, formats (webp/avig/jpg..)
Let me check the knowledge base for you again. One moment! :books:
Yes, the same responsive image system applies to the <Picture /> component.
-
Responsive image behavior &
layout
<Picture />“accepts all the properties of the<Image />component,” which includes thelayoutprop ('constrained' | 'full-width' | 'fixed' | 'none'). [Picture reference; layout prop]
Whenlayoutis set, Astro will generatesrcset/sizesand apply responsive behavior, just like for<Image />. [Responsive behavior] -
Responsive image styles (
image.responsiveStyles)
These global styles are applied “when the layout property is set” and are described generically for “images,” not limited to<Image />. [Responsive image styles]
Since<Picture />uses the samelayoutsystem and its inner<img>gets the samedata-astro-imageattributes, those styles apply there as well. -
Formats (
formats,fallbackFormat)
<Picture />is specifically designed to output multiple formats from a single source image via theformatsprop (e.g.['avif', 'webp']) and afallbackFormatfor the<img>tag. [Picture guide; Picture reference]
So with <Picture /> you can:
- Use
layoutand get responsivesrcset/sizes. - Benefit from responsive image styles when
image.responsiveStyles: trueis configured. - Generate multiple formats (
webp,avif,jpg/pngfallback) viaformatsandfallbackFormat.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt If I am using images in /public folder with Picture component, does Responsive image behavior works? Can I use layout, Responsive image styles, formats (webp/avig/jpg..)
Let me check the knowledge base for you again. One moment! :books:
The knowledge sources don’t explicitly document responsive behavior for <Picture /> when its src is an image in /public, so some of this is uncertain.
What the docs do say:
-
Public images are never optimized or responsive
“Images in yourpublic/folder are never optimized, and responsive images are not supported.” [Responsive behavior]
This statement is general (not limited to<Image />), so it applies to<Picture />as well. -
<Picture />accepts all<Image />props, includinglayout
<Picture />“accepts all the properties of the<Image />component plus” its own props. [Picture reference]
layoutis one of those<Image />props. [layout] -
image.responsiveStylesonly apply whenlayoutis used
image.responsiveStylesadds global CSS for responsive images “whenlayoutis set to 'constrained', 'full-width', or 'fixed' using the configuration or thelayoutprop on the image component.” [Responsive image styles] -
Formats are for transformed images
<Picture />usesformatsandfallbackFormatto generate multiple formats from a source image. [Picture reference]
But public images “are never optimized,” which implies no transformation into other formats for/publicsources. [Responsive behavior]
Putting this together for /public + <Picture />:
- You can technically pass
layout,formats, etc. because<Picture />accepts the same props, but:- The docs explicitly say responsive images are not supported for
public/images, so automatic responsive behavior (srcset/sizesgeneration) should not be expected to work for/publicsources. [Responsive behavior] - Likewise,
image.responsiveStylesare tied to that responsive behavior; the docs don’t state they apply to/publicimages, and they emphasize thatpublic/images are not responsive. [Responsive image styles; Responsive behavior] - Format transformation (
formats,fallbackFormat) depends on optimization/processing, which the docs say does not happen forpublic/images. [Responsive behavior]
- The docs explicitly say responsive images are not supported for
So, based strictly on the docs:
- Responsive image behavior (
layout, generatedsrcset/sizes) is not supported for images in/public. image.responsiveStylesand format conversion (formats,fallbackFormat) are documented only for optimized images, andpublic/images are explicitly “never optimized,” so you should not rely on those features for/publicsources.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt how to check astro's files on build? I wanna check that all links have absolute path on astro on build
Let me check the knowledge base for you again. One moment! :books:
The knowledge sources provided don't contain enough information to directly answer how to inspect or audit built files for absolute paths in Astro.
What the docs do mention that's relevant:
- Build output directory: By default,
astro buildwrites output to./dist. You can change this with theoutDirconfig option. [outDir reference] - Integration hooks: The
astro:build:donehook fires after a production build completes and gives you access to generated pages and assets. You could use this hook to write a custom integration that inspects the built HTML files for link paths. [astro:build:done]
For example, the hook provides:
dir— aURLto the build output directorypages— a list of all generated pages with theirpathname
'astro:build:done': async ({ dir, pages }) => {
// Use fileURLToPath + fs to read built HTML files and inspect links
}
However, the knowledge sources don't document a built-in Astro feature specifically for auditing links in the build output. For that kind of link checking, you'd likely need a third-party tool (e.g. broken-link-checker, lychee, or a custom script) run against the dist/ folder — but that's outside the scope of what the provided Astro docs cover.
If you want to ensure internal links use the correct base path, the docs recommend configuring base and prefixing all internal links with import.meta.env.BASE_URL. [base config]
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt reference all the docs that are related to creation of custom integration. The goal is to create an integration that would check whether my pages contain hyphens only in slug, whether all links in the project are absolute urls and npt relevant and so on
Let me check the knowledge base for you again. One moment! :books:
Here are the relevant docs from the knowledge sources for building a custom Astro integration that inspects your build output:
Core Integration API References
1. Integration API Overview
The full API reference for writing integrations is at [Astro Integration API]. It covers all hooks, types, and options available.
2. Quick API Reference
A concise summary of all hooks and their signatures: [Quick API Reference]
3. Building Your Own Integration (Guide)
A high-level guide to get started: [Building your own integration]
Relevant Hooks for Your Use Case
astro:config:setup
Runs on initialization. Useful for setting up your integration and accessing the Astro config. [astro:config:setup]
astro:build:done
This is the most relevant hook for your goal. It fires after the production build completes and gives you:
dir— aURLto the build output directory (so you can read built HTML files)pages— a list of all generated pages with theirpathnameassets— aMap<string, URL[]>of output file paths grouped by route pattern
Example skeleton:
import { writeFile } from 'node:fs/promises';
import { fileURLToPath } from 'node:url';
export default function myLinkCheckerIntegration() {
return {
name: 'my-link-checker',
hooks: {
'astro:build:done': async ({ dir, pages, logger }) => {
for (const page of pages) {
// page.pathname gives you the route, e.g. "/blog/my-post"
// Use dir + pathname to read the built HTML and inspect links/slugs
const filePath = fileURLToPath(new URL(.${page.pathname}index.html, dir));
// ... read file, parse HTML, check links and slugs
logger.info(Checked: ${page.pathname});
}
}
}
}
}
### `astro:build:generated`
Fires after static build routes and assets are generated, **before** cleanup. Useful if you need to access files before they might be cleaned up. [[astro:build:generated](<https://docs.astro.build/en/reference/integrations-reference/#astrobuildgenerated>)]
### `astro:build:setup`
Gives access to the Vite config and a `pages` Map with `PageBuildData` (including `route.pattern` and `route.type`) just before the build runs. [[astro:build:setup](<https://docs.astro.build/en/reference/integrations-reference/#astrobuildsetup>)]
---
## Logging
Use `AstroIntegrationLogger` (available in every hook as `logger`) to report issues found during your checks: [[AstroIntegrationLogger](<https://docs.astro.build/en/reference/integrations-reference/#astrointegrationlogger>)]
```js
logger.warn(`Non-absolute link found on page: ${page.pathname}`);
logger.error(`Slug contains non-hyphen characters: ${page.pathname}`);
Custom Hooks (Optional)
If you want to expose your integration's events to other integrations, you can define custom hooks: [Custom hooks]
Community Resources
For templates and examples to bootstrap your integration: [Community Resources]
Summary for your specific goals:
- Check slugs for hyphens only: Use
astro:build:done→ iteratepages, inspectpage.pathname. - Check all links are absolute URLs: Use
astro:build:done→ read built HTML files fromdir, parse<a href>tags. - The knowledge sources don't document a built-in Astro feature for link auditing — you'd implement the HTML parsing logic yourself (e.g. with Node's
fs+ an HTML parser) inside the hook.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt only unique pages, no fragments in url
Let me check the knowledge base for you again. One moment! :books:
Based on the conversation context, you're asking about filtering to only unique pages (no URL fragments) when iterating over pages in the astro:build:done hook.
The pages array in astro:build:done provides { pathname: string }[] — each entry is a generated page path. [astro:build:done]
The knowledge sources do not explicitly document whether pages can contain duplicate entries or fragment-based entries (#...). However, based on what the docs say, pathname represents "the finalized path of the page," which corresponds to actual generated files — not URL fragments (fragments are client-side only and never correspond to separate generated files).
For your integration, you could defensively filter anyway:
'astro:build:done': async ({ dir, pages, logger }) => {
const seen = new Set();
const uniquePages = pages.filter(({ pathname }) => {
// Exclude any fragment-based entries (just in case)
if (pathname.includes('#')) return false;
// Deduplicate
if (seen.has(pathname)) return false;
seen.add(pathname);
return true;
});
for (const page of uniquePages) {
// inspect page.pathname for slugs, links, etc.
logger.info(`Checking: ${page.pathname}`);
}
}
That said, the knowledge sources don't explicitly state that pages can contain fragments or duplicates — so this filtering is a precaution rather than a documented necessity.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt nope, I want only unique pages with docs that are related to creation of integration
Let me check the knowledge base for you again. One moment! :books:
Looking at the knowledge sources, the astro:build:done hook provides pages as { pathname: string }[] — a list of all generated pages. [astro:build:done]
The docs do not explicitly state whether pages can contain duplicates, but the pathname represents "the finalized path of the page," which maps to actual generated files. To get unique pages, you can deduplicate by pathname:
export default function myIntegration() {
return {
name: 'my-link-checker',
hooks: {
'astro:build:done': async ({ dir, pages, logger }) => {
const uniquePages = [...new Map(pages.map(p => [p.pathname, p])).values()];
for (const page of uniquePages) {
logger.info(`Checking: ${page.pathname}`);
// Read built HTML from dir + pathname, then inspect slugs/links
}
}
}
}
}
The logger (from AstroIntegrationLogger) is available in every hook to report findings. [AstroIntegrationLogger]
The dir option gives you a URL to the build output directory, which you can combine with page.pathname to locate and read the built HTML files. [astro:build:done]
The knowledge sources do not document any built-in deduplication of the pages array, nor do they mention fragment-based entries — the filtering above is a precaution not explicitly required by the docs.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt still fail. I want you to give me links to astro.build docs. Find me unique pages of astro.build website, that are talking about creation of integration for astro website. Do not send me urls of docs with fragments like this: site.com/docs#fragment
Let me check the knowledge base for you again. One moment! :books:
Here are the unique Astro docs pages (no fragment-only URLs) from the knowledge sources that are directly about creating integrations:
- Astro Integration API — the full reference for writing integrations (hooks, types, options)
- Working with integrations — the guide covering how to use and build integrations
- Astro Adapter API — covers building a special kind of integration (adapters) for SSR
- Create a dev toolbar app — a recipe showing how to build an integration that adds a dev toolbar app
- Publishing your integration to npm — covered within the integrations guide above (same base URL)
- Building Astro DB integrations — covers extending Astro DB via integrations
Note: The knowledge sources I have access to are limited to the documents provided to me. There may be additional relevant pages on astro.build that are not represented in my knowledge sources, and I cannot confirm or list those.
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
@haughty onyx Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@crystal cobalt new question, explain me that behavior, I added a js script that has to execute client side (custom preload for a href tags), it is present on every page. I put it in the bottom of the body tag. But when I do check the deployed website, I am seeing the script is loaded in two hops in network dependency tree.
here are those hops:
/_astro/index.astro_ast….C8-jhcFZ.js(mysite.com) - 246 ms, 0.86 KiB
/_astro/404.astro_ast….8_t5TdLb.js(mysite.com) - 353 ms, 1.33 KiB
Here is the containment of the first url:
import"./404.astro_astro_type_script_index_0_lang.8_t5TdLb.js";
here is the containment of the second:
(function(){const c=new Set;function s(){if("connection"in navigator){const e=navigator.connection;return e.saveData||/2g/.test(e.effectiveType)}return!1}function f(e){if(!navigator.onLine)return!1;try{const t=new URL(e,location.href);return location.origin===t.origin&&(location.pathname!==t.pathname||location.search!==t.search)&&!c.has(e)}catch{return!1}}function a(e,t){if(e=e.replace(/#.*/,""),!!f(e)&&!(!t&&s()))if(c.add(e),document.createElement("link").relList?.supports?.("prefetch")){const n=document.createElement("link");n.rel="prefetch",n.setAttribute("href",e),document.head.append(n)}else fetch(e,{priority:"low"})}const r=new WeakMap,l=new IntersectionObserver((e,t)=>{for(const n of e){const o=n.target;if(n.isIntersecting)r.set(o,setTimeout(()=>{t.unobserve(o),r.delete(o),a(o.href)},300));else{const i=r.get(o);i&&(clearTimeout(i),r.delete(o))}}});for(const e of document.querySelectorAll("a[data-prefetch]"))l.observe(e);for(const e of["touchstart","mousedown"])document.addEventListener(e,t=>{const n=t.target.closest("a[data-prefetch]");n&&a(n.href,!0)},{passive:!0})})();
Let me check the knowledge base for you again. One moment! :books:
Looking at the second file's content, that is actually Astro's built-in prefetch script — not your custom script. It contains the prefetch logic for a[data-prefetch] links, which matches exactly what Astro injects when you enable the prefetch feature. [Prefetch guide]
What's happening
The two-hop loading pattern you're seeing is Astro's script bundling and deduplication behavior:
-
First hop (
index.astro_ast….js): A small module that just re-exports/imports the actual script — this is Astro's deduplication mechanism. By default, Astro processes<script>tags, bundles them, and deduplicates them so a script shared across pages is only included once. [Script processing] -
Second hop (
404.astro_ast….js): The actual bundled script content. The file is named after404.astrobecause that's likely where Astro first encountered and bundled this script (or it's the canonical bundle entry point Astro chose).
This is expected behavior — Astro bundles scripts as type="module" and uses ES module imports for deduplication. [Script processing]
Why it looks like "two hops"
Because processed <script> tags become ES modules (type="module"), the browser must:
- Fetch the entry module
- Parse it, find the
importstatement - Fetch the imported module
This is normal ES module loading behavior.
What you can do
If you want to avoid this two-hop behavior and have the script render exactly as written (single file, no bundling), use is:inline:
<script is:inline>
// Your prefetch script here — rendered as-is, no bundling
</script>
However, is:inline means: no TypeScript, no npm imports, no deduplication, and the script will appear as many times as the component is rendered. [is:inline directive]