#I need to replace a component in Content Collection renderer How do I do ?
25 messages · Page 1 of 1 (latest)
When using Content Collections the <Content> component is accessed a bit differently vs directly importing the file. Here is an example of how to access the<Content> component when using Content Collections: https://docs.astro.build/en/guides/content-collections/#rendering-content-to-html
Yeah I am importing correctly the Content element but adding my custom compontent like this <Content components={{img: Picture }}/> does nothing 😦
and typing is not helping me to find is the component is named differently
What does your code look like?
the page looks like this
---
import { getCollection } from 'astro:content';
import Picture from 'components/Picture.astro'
import Article from 'layouts/Article.astro'
export const prerender = true
// 1. Generate a new path for every collection entry
export async function getStaticPaths() {
const blogEntries = await getCollection('projects');
return blogEntries.map((entry) => ({
params: { project: entry.slug }, props: { entry },
}))
}
// 2. For your template, you can get the entry directly from the prop
const { entry } = Astro.props as Awaited<ReturnType<typeof getStaticPaths>>[0]['props'];
const { Content } = await entry.render();
---
<Article title={entry.data.title} image={[entry.data.image]} description={entry.data.description} link={entry.data.link} breadcrumb={[{text: 'Accueil', href: '/'}, {text: 'Projets', href: '/projets'}, {text: entry.data.title}]}>
<h1>{entry.data.title}</h1>
<p class="flex justify-end font-lights my-0">
{entry.data.created && (
<span>Sortie initial le {entry.data.created.toLocaleDateString('fr')}</span>
)}
<!-- <p>Software updated: {entry.data.updated.toLocaleDateString()}</p> -->
</p>
<Content components={{img: Picture }}/>
</Article>
and the Picture Component Wrap around both the HTMLImageElement and the Image element of Astro
but add features like AVIF and WebP support in a picture element
Ahh I see, what does the <Picture> component looks like?
---
import { getImage } from 'astro:assets'
import AstroUtils from '../libs/AstroUtils'
import { objectOmit } from '@dzeio/object-util'
const formats = [
'avif',
'webp'
]
export interface Props extends Omit<astroHTML.JSX.ImgHTMLAttributes, 'src'> {
src: ImageMetadata | string
srcDark?: ImageMetadata | string
width?: number
height?: number
}
type PictureResult = {
format: 'new'
formats: Array<{format: string, img: Awaited<ReturnType<typeof getImage>>}>
src: Awaited<ReturnType<typeof getImage>>
} | {
format: 'raw'
src: string
}
interface Result {
light: PictureResult
dark?: PictureResult | undefined
}
async function resolvePicture(image: ImageMetadata | string): Promise<PictureResult> {
const ext = typeof image === 'string' ? image.substring(image.lastIndexOf('.')) : image.format
if (ext === 'svg') {
return {
format: 'raw',
src: typeof image === 'string' ? image : image.src
}
}
const imageFormats: Array<{format: string, img: Awaited<ReturnType<typeof getImage>>}> = await Promise.all(
formats.map(async (it) => ({
img: await getImage({src: Astro.props.src, format: it, width: Astro.props.width, height: Astro.props.height}),
format: it
}))
)
const orig = await getImage({src: Astro.props.src, format: ext, width: Astro.props.width, height: Astro.props.height})
return {
format: 'new',
formats: imageFormats,
src: orig
}
}
const res = await AstroUtils.wrap<Result>(async () => {
return {
light: await resolvePicture(Astro.props.src),
dark: Astro.props.srcDark ? await resolvePicture(Astro.props.srcDark) : undefined
}
})
const props = objectOmit(Astro.props, 'src', 'srcDark', 'class')
---
{res.light.format === 'new' && (
<picture {...props} {...res.light.src.attributes} class:list={[res.light.src.attributes.class, Astro.props.class, {'dark:hidden': res.dark}]}>
{res.light.formats.map((it) => (
<source srcset={it.img.src} type={`image/${it.format}`} />
))}
<img src={res.light.src.src} />
</picture>
) || (
<img {...props} class:list={[Astro.props.class, {'dark:hidden': res.dark}]} src={res.light.src as string} />
)}
{res.dark && res.dark.format === 'new' && (
<picture {...props} {...res.dark.src.attributes} class:list={[res.dark.src.attributes.class, Astro.props.class, 'hidden', 'dark:block']}>
{res.dark.formats.map((it) => (
<source srcset={it.img.src} type={`image/${it.format}`} />
))}
<img src={res.dark.src.src} />
</picture>
) || (res.dark && (
<img {...props} class:list={[Astro.props.class, 'hidden', 'dark:block']} src={res.dark.src as string} />
))}
it's a big boy
but it works perfectly in my other places
That is weird 🤔, are you able to override other tags like <h1>?
it doesn't seems to work
Changed h2 to the astro file below but it is still an H2 in the rendered HTML
<p class="POKEMON"><slot /></p>
the whole source code is available here if you want:
https://github.com/Aviortheking/avior.me
the Content Collection rendering is done here src/pages/projets/[project].astro the collection is here src/content/projects
also the Picture component is here src\components\Picture.astro
Ahh I see the problem, the content collection is using .md files but this is only compatible with .mdx files
ok, I will try to convert my files to MDX
it is working, will it also work for .md files in the future ?
AFAIK no, MDX allows you to use components but regular markdown does not. Although, it is possible to transform the HTML generated from regular markdown using a remark or rehype plugin:
https://github.com/remarkjs/remark
https://github.com/rehypejs/rehype/tree/main
ok Thanks you very much for your help !
I'll check if I go the route of full MDX or try to use remark to update content
Just something else, How does the Content Collection works under the hood ? is it using remark to process the files ?
if so is it possible to add some pre processing to change components ?
by an integration or something else ?