A Remark plugin that converts Markdown <img> elements into modern <picture> elements with responsive and next-gen image formats (WebP, AVIF, etc.).
Works great together with picture-converter β a companion tool that converts your legacy
.png,.jpg, and.giffiles into efficient next-gen formats.
Traditional Markdown images are static and canβt take advantage of modern browser optimizations. With remark-picture, you can:
β
Automatically generate <picture> elements from 
β
Serve WebP and AVIF images for smaller size and faster loading
β
Define responsive sources via media queries
β
Add pixel-density variants (1x, 2x, etc.)
β
Use custom URL templates to integrate with CDNs or build pipelines
β
Stay fully typed with TypeScript
npm install remark-pictureimport {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
import remarkPicture from 'remark-picture'
const file = await unified()
.use(remarkParse)
.use(remarkPicture)
.use(remarkRehype)
.use(rehypeStringify)
.process('')
console.log(String(file))<picture>
<source srcset="images/sunset.avif" type="image/avif">
<source srcset="images/sunset.webp" type="image/webp">
<img src="images/sunset.jpg" alt="Sunset">
</picture><picture>
<source srcset="/mobile/sunset.webp, /mobile/[email protected] 2x"
type="image/webp" media="(max-width: 480px)">
<source srcset="/desktop/sunset.webp, /desktop/[email protected] 2x"
type="image/webp" media="(min-width: 481px)">
<img src="images/sunset.jpg" alt="Sunset">
</picture>import {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
import remarkPicture from 'remark-picture'
const file = await unified()
.use(remarkParse)
.use(remarkPicture, {
formatMapping: {
jpg: ['avif', 'webp'],
png: ['webp']
},
media: {
mobile: {
urlTemplate: '/mobile/{basename}.{ext}',
query: '(max-width: 480px)',
pixelDensity: [1, 2]
},
desktop: {
urlTemplate: '/desktop/{basename}.{ext}',
query: '(min-width: 481px)',
pixelDensity: [1, 2]
}
}
})
.use(remarkRehype)
.use(rehypeStringify)
.process('')
console.log(String(file))| Option | Type | Default | Description |
|---|---|---|---|
formatMapping |
Record<ImageExtension, ImageExtension[]> |
{ jpg/png β avif, webp } |
Defines which formats to generate from which source |
media |
Record<string, { urlTemplate, query?, pixelDensity? }> |
β | Define responsive sources (e.g. mobile vs desktop) |
pixelDensity |
number |
1 |
Default pixel density multiplier |
imageUrlTemplate |
string |
β | Template for generated image URLs, e.g. "/images/{basename}@{pd}x.{ext}" |
imageToPicture |
(image, convert, pictureBuilder) => Picture |
β | Custom function for full control over generation |
Template strings (for imageUrlTemplate or media.urlTemplate) can use:
| Placeholder | Example | Description |
|---|---|---|
{dir} |
/assets/images/ |
Original image directory |
{basename} |
photo |
Filename without extension |
{originalExt} |
jpg |
Original image extension |
{ext} |
webp |
Target image extension |
{pd} |
2 |
Pixel density (e.g. 1, 2) |
{basepath} |
/assets/images/photo |
Directory + basename |
Example:
imageUrlTemplate: "/cdn/{basename}@{pd}x.{ext}"You can fully control how <picture> elements are generated:
const adaptiveOptions: Options = {
formatMapping: {png: ['webp']},
media: {
mobile: {
query: '(max-width: 480px)',
urlTemplate: '{dir}mobile/{basename}-{pd}x.{ext}',
pixelDensity: [2, 3]
},
desktop: {
query: '(min-width: 481px)',
urlTemplate: '{basepath}.{ext}'
}
}
}
function customImageToPicture(image: Image, convert: ConvertFn): Picture | PictureBuilder | undefined {
if (image.url.includes('icon')) {
// skip conversion for icons
return undefined
}
if (image.url.startsWith('adaptive-images/')) {
// use custom options
return convert(adaptiveOptions)
}
// use default behavior
return convert()
}
...
use(remarkPicture, {imageToPicture: customImageToPicture}){
png: ['avif', 'webp'],
jpg: ['avif', 'webp'],
jpeg: ['avif', 'webp'],
gif: ['webp'],
apng: ['webp']
}Contributions, issues, and feature requests are welcome!
Feel free to open an issue or
submit a pull request.
Picture Converter β converts old
image formats (.png, .jpg, .gif) to modern, efficient formats (.webp, .avif) automatically.