Skip to content

Astro, embed an image in markdown without relative path

I hit this problem today: you know, you markdown we can embed images like this:


or using relative paths:


Imagine you have a folder with a markdown and in the same folder file.png, and you want to include that image.

Apparently Astro can’t work with this syntax to include a file in the same folder as the markdown file, which is valid markdown and works in most apps and CMS:


Instead, it requires you to use a ./ relative path


Usually it’s not a problem for me as all images are in a separate folder under /public, served statically.

But for a project I’m working on, which has a TON of markdown files, I grew tired of using VS Code to edit markdown, and I was looking for a good markdown editor that could open a folder full of markdown files, and also visually display images inside the content.

I settled on Obsidian, which I once tried for note taking but didn’t stick to it.

I found it an excellent markdown editor, with lots of options to customize it as I want it to behave.

Problem: Obsidian stores images like this in Markdown


Astro requires you to use a ./ relative path


No way to change either of them.

After some googling I found a StackOverflow answer that guided me in the right direction, because I learned I could use a remark plugin to change its behavior, and I could add that to the Astro config in astro.config.mjs like this:

import { defineConfig } from "astro/config"

export default defineConfig({
  markdown: {
    remarkPlugins: [
      [SOMEPLUGIN, { 

Now the problem was finding a plugin for my needs.

I don’t know how but found this: which lets you add an absolute URL to each markdown image, to use a CDN or something.

Pretty similar to my needs.

So I wrote something similar, directly in my Astro config file:

//npm install unist-util-visit

import { visit } from 'unist-util-visit'

function fixRelativeLinksFromObsidianToAstro(options) {
  function visitor(node) {
    if (node.url.startsWith('http') || node.url.startsWith('/images/')) {

    if (!node.url.startsWith('/')) {
      node.url = './' + node.url

  function transform(tree) {
    visit(tree, 'image', visitor)

  return transform

and I used this in my Astro config:

export default defineConfig({
  markdown: {
    remarkPlugins: [[fixRelativeLinksFromObsidianToAstro, {}]],

Now even ![](file.png) images work in Astro.

→ Download my free JavaScript Handbook!



You might be interested in those things I do:

  • Learn to code in THE VALLEY OF CODE, your your web development manual
  • Find a ton of Web Development projects to learn modern tech stacks in practice in THE VALLEY OF CODE PRO
  • I wrote 16 books for beginner software developers, DOWNLOAD THEM NOW
  • Every year I organize a hands-on cohort course coding BOOTCAMP to teach you how to build a complex, modern Web Application in practice (next edition February-March-April-May 2024)
  • Learn how to start a solopreneur business on the Internet with SOLO LAB (next edition in 2024)
  • Find me on X

Related posts that talk about astro: