Markdown and store image

Hello good!

I’ve been seeing this on the forum but got nowhere. Let’s take Bear app for example, it uses markdown and when you insert an image on the markdown document it is synchronized and does not save the path, but saves the image also within the note.

At DEVONthink I find it confusing. You save an image locally but if you remove it, it is no longer visible. If you store it inside devonthink you can see it, but when you go to DEVONthink Go it seems that it doesn’t show it either despite being inside devonthink.

How do you make an image look at a markdown on mac and iOS and not lose the path?

All the best!

Bear creates a package or a zip file or whatever. That’s fine, but not inherent to markdown: markdown uses URLs to images, just like HTML. The only portable way to include images in MD is by using a data URL. Which has certain disadvantages, too. How to keep MD and images together in DT has been discussed numerous times here, one of the preferred solutions is to store images and MD in the same group.

DT offers to store all images in a single place since 3.8.

1 Like

Thank you friend!

Can you say me how can I do this with DT? “DT offers to store all images in a single place since 3.8.”

Preferences > Files > Markdown > Images: Import assets to group


Thank you! =)

Y gracias para traducir mi repuesta😉

I am a bit uncertain what “single place” means. Going by the screenshot of the Markdown Pref Pane shown above, using the location Assets means the “single place” refers to all images from all Markdown docs in one group go into the subgroup Assets.

Doesn’t this create problems if one absentmindedly moves the md file, but does not bring the assets along? In addition, if that single assets folder contains images from many md docs in that group, and one moves only some of them, how to disentangle that (in the worst case, just duplicate the whole assets group to the new location of a file, since the unused dead wood in there does no harm, but that’s a bit inelegant).

I prefer to have those images all in a group based at the root of the DB. So I tried to replace Asset in the md pref pane by /MdImg, and indeed, an image dragged or copied onto a md file will be stored there. So this seems to work and rids me of the fear of losing sight of all those Asset folders (which I also don’t want to clog up my group structure).

I noted that the md path for the Asset subgroup is done by filename, e.g.
whereas if I choose the root location /MdImg, the reference will be by x-devonthink-item. Is that to avoid having name collisions in such “single group for all md assets in whole DB” scenarios? In any case, I find the latter safer, because it is unique, independent of filename.

Added bonus: If the images in the root group /MdImage are referenced by x-item, then, if I browse the images in the MdImg folder, I can find the backlinks to the md files where they are used in the Documents→Links→Incoming Links info pane. That’s pretty awesome.

1 Like

No necessarily. It works for people who are working with Markdown in groups, not just put in the root of the database.

Actually, while your suggestion is fine (especially as it suits your purpose), I’d say there’s a bigger danger of moving the file but not the assets group since they’re not segregated into a single group together.

Fair enough, but what’s so special about pooling image assets for a group? That’s chaining a file asset to a group. “Document” is the basic entity that can migrate around in a DB and the assets should either (a) be built-in (like Word or Pages docs), (b) follow transparently along, or (c) are in a fixed location. I think that’s why annotation files are in a fixed DB location, and Markdown images look like a very similar case. Having said that, DT gives the flexibility to use a fixed location, but I wonder how many people get around putting a slash in front of the asset folder location in the pref pane.

but what’s so special about pooling image assets for a group? That’s chaining a file asset to a group. “Document” is the basic entity that can migrate around in a DB and the assets should either (a) be built-in (like Word or Pages docs), (b) follow transparently along, or (c) are in a fixed location.

They are in a fixed location when they’re in an asset subgroup.

You are approaching this subject only as you see it. Not everyone uses a database as one big playground for all files to wander. There are people who use a database like a neighborhood, populating it with segregated clusters of data that aren’t necessaruly to be shared. In this case, segregating documents and assets in one group is a very useful tool for them.

1 Like

Well, if the assets are built in or not depends very much on the document, not at all on the database. As has already been discussed many times.

I added my way of seeing it. I’m not arguing that the assets must be in a fixed location. Just noted that this option is kind of non-obvious in the prefs (vs. say a choice between “with doc, with group, with DB”).

And then, one day, they need to split one of their groups into two fire-walled ones? And have to pick the assets subgroup apart?

Anyway, this is all good. The asset mechanism is super-useful and has made md with images viable to me in DT. I tried to point out that with group-based assets, inconspicuous DT operations like “move item” can break the integrity of a document (md file plus assets).

Not sure what this means. I am not discussing the pros and cons of asset integration. I pointed out that chaining assets to groups while still allowing the user to move files into another group (and it is irrelevant in this context that the “segregated cluster” crowd chooses not to do that) will break the integrity of the document. That’s a fact. I’m not aware of another situation in DT where using a common command breaks a document.

I provided a suggestion for a mechanism that avoids that, should your DB organization allow that. Incidentally, my scheme will suffer from exactly the same problem when a document is moved into another DB. In that case, I believe, the only safe method is “1 document = 1 file”.

Thank you very much for sharing this! I think I also tried to set a location for the Import images to group… preference when the feature became available but it wasn’t possible back then (or maybe I did it wrong). It’s great that it’s possible now!

However, there’s a problem:

  • If the Markdown record and the Assets group are both located in the database’s root, then DEVONthink does not use the image’s item link (i.e. its “x-devonthink-item” link).

  • Instead it inserts a relative link.
    (I guess that’s because using a relative link is the normal behaviour for records that have the same location as the Assets group.)

That’s unfortunate as it makes the usage of /Assets (i.e. usage of a location instead of a name) somehow inconsistent. @cgrunenberg could this be changed, i.e. if the preference starts with a slash then always use the item link?

@gg378 it’s probably possible to create a Smart Rule script that moves linked images into the database into which a Markdown record was moved. Almost there but still not sure.

1 Like

Currently the used link depends on the location of the image and the document - if possible a relative link is preferred as that’s more compatible to other apps.

1 Like

This Smart Rule script moves linked images to the record’s new database.

It’s important that you set e.g. /Assets in preferences, see below. It won’t work if there’s no slash.


The script checks

  • whether an image is also linked in other records
  • whether there’s already an identical image in the new database’s Assets group
    (“identical image” means an image that would be a duplicate if both images shared a database)

Depending on these checks different things happen:

  • If only 1 record links to the image

    • If there’s no identical image in the new database’s Assets group:
      Move image to new database’s Assets group

    • If there’s an identical image in the new database’s Assets group:
      Replace link URL with identical image’s reference URL and move unnecessary image to trash

  • If more than 1 record links to the image

    • If there’s no identical image in the new database’s Assets group:
      Duplicate image and replace link URL with duplicated record’s reference URL

    • If there’s an identical image in the new database’s Assets group:
      Replace link URL with identical image’s reference URL



  • Go to preferences Files > Markdown
  • Set a location for Import images to group… , e.g. /Assets
    Note the slash at the start.

Script properties


Set property theRecordHistory_Name_End to your Record History records’ name end. If the name is 2022-03-10 My daily record history then set the property to My daily record history.

The script uses the linked image’s incoming references. If you use the “Record History” script and open an image then it’s added to the Record History, which means the Record History record is also added to the image’s incoming references. Because the Record History and the records this script was written for both are Markdown there’s no way to distinguish between them other than filtering by name.

Smart Rule

Create a Smart Rule:

  • Search in: Databases
  • Matching:
    • All
      • Content: ![
      • Content: x-devonthink-item://
      • Kind: Markdown
  • Event: On Moving
  • Action: Execute Script (AppleScript)


-- Smart Rule script - Move Markdown records' linked images

property theImageDestinationLocation : "/Assets" -- Note the "/" at the start. This script is build for Assets groups that live in databases' root.
property theRecordHistory_Name_End : "Record History" -- If you use the "History Record" script then it's necessary to set this property to the History Record's name end (e.g. "Record History")

on performSmartRule(theRecords)
	tell application id "DNtp"
			repeat with thisRecord in theRecords
				set thisRecord_Text to plain text of thisRecord
				--------------------------------------------------- Create Assets group in new database ----------------------------------------------------
				set theImageDestinationGroup_NewDatabase to create location theImageDestinationLocation in database of thisRecord
				--------------------------------------------------- Get Assets group in old database ----------------------------------------------------
				set thisRecord_Text_TextItems to my tid(thisRecord_Text, "![")
				set theImageDestinationGroup_OldDatabase to missing value
				repeat with thisTextItem in thisRecord_Text_TextItems
					set thisTextItem to thisTextItem as string
					if thisTextItem contains "](" then
						set thisTextItem_Part to ((characters ((offset of "](" in thisTextItem) + 2) thru -1) in thisTextItem) as string
						set thisLinkURL to ((characters 1 thru ((offset of ")" in thisTextItem_Part) - 1)) in thisTextItem_Part) as string
						if thisLinkURL starts with "x-devonthink-item://" then
							set thisImage to (get record with uuid thisLinkURL)
							set thisImage_LocationGroup to location group of thisImage
							if ("/" & (name of thisImage_LocationGroup)) = theImageDestinationLocation then
								set theImageDestinationGroup_OldDatabase to thisImage_LocationGroup
								exit repeat
							end if
						end if
					end if
				end repeat
				if theImageDestinationGroup_OldDatabase ≠ missing value then
					if theImageDestinationGroup_NewDatabase ≠ theImageDestinationGroup_OldDatabase then
						set thisRecord_Text_new to thisRecord_Text
						----------------------------------- Get images in old database's Assets group that link to record ------------------------------------
						set theImages to (children of theImageDestinationGroup_OldDatabase whose incoming references contains thisRecord)
						repeat with thisImage in theImages
							------------------------------ Check whether there's an identical image in new database's Assets group -------------------------------
							set theImageDestinationGroup_NewDatabase_Children to (children of theImageDestinationGroup_NewDatabase whose content hash = content hash of thisImage)
							if theImageDestinationGroup_NewDatabase_Children = {} then
								set theExistingDuplicateImage_ReferenceURL to missing value
								set theExistingDuplicateImage_ReferenceURL to (reference URL of item 1 of theImageDestinationGroup_NewDatabase_Children) as string
							end if
							------------------------------------------------ Check how many records link to the image -------------------------------------------------
							set thisImage_IncomingReferences to (incoming references of thisImage whose name without extension does not end with theRecordHistory_Name_End)
							set thisImage_IncomingReferences_Count to (count of thisImage_IncomingReferences)
							if thisImage_IncomingReferences_Count = 1 then
								----------------------------------------------- Move image to new database's Assets group ------------------------------------------------
								if theExistingDuplicateImage_ReferenceURL = missing value then
									set thisImage to move record thisImage to theImageDestinationGroup_NewDatabase
									-------------------- Replace link URL with identical image's Reference URL and move unnecessary image to trash ---------------------
								else if theExistingDuplicateImage_ReferenceURL ≠ missing value then
									set thisRecord_Text_new to my tid(my tid(thisRecord_Text_new, (reference URL of thisImage) as string), theExistingDuplicateImage_ReferenceURL)
									move record thisImage to trash group of database of thisImage
								end if
							else if thisImage_IncomingReferences_Count > 1 then
								----------------------------- Duplicate image and replace link URL with duplicated record's Reference URL ------------------------------
								if theExistingDuplicateImage_ReferenceURL = missing value then
									set thisDuplicatedImage to duplicate record thisImage to theImageDestinationGroup_NewDatabase
									set thisRecord_Text_new to my tid(my tid(thisRecord_Text_new, (reference URL of thisImage) as string), (reference URL of thisDuplicatedImage) as string)
									----------------------------------------- Replace link URL with identical image's Reference URL ------------------------------------------
								else if theExistingDuplicateImage_ReferenceURL ≠ missing value then
									set thisRecord_Text_new to my tid(my tid(thisRecord_Text_new, (reference URL of thisImage) as string), theExistingDuplicateImage_ReferenceURL)
								end if
							end if
						end repeat
						if thisRecord_Text_new ≠ thisRecord_Text then set plain text of thisRecord to thisRecord_Text_new
					end if
					log message info "Smart Rule: Couldn't find the moved record's old \"Assets\" group" record thisRecord
				end if
			end repeat
		on error error_message number error_number
			if the error_number is not -128 then display alert "DEVONthink" message error_message as warning
		end try
	end tell
end performSmartRule

on tid(theInput, theDelimiter)
	set d to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	if class of theInput = text then
		set theOutput to text items of theInput
	else if class of theInput = list then
		set theOutput to theInput as text
	end if
	set AppleScript's text item delimiters to d
	return theOutput
end tid
1 Like

Wow, @pete31 , is there anything you can’t write a script for :smile: ? This is good to know. I also realized that if the assets are stored as x-items, even if the md file is moved to another DB, as long as all DBs are open, the asset can still be called up.

One downside of the DB-root x-item asset storage: A web page export of a group will not bring along the assets. I haven’t tried, but presumably with per-group storage that would work.

In the end, none of this is unsolvable and could be easily integrated into DT. This shows that the “asset” mechanism was a quick bolt-on that has not the same level of integration and consistency as all the other mechanisms in DT. I guess there was big pressure from parts of the md crowd to make decent image management possible. And the solution is definitely decent.

I should not end this with a Markdown rant, but here I go (disclaimer: I really like md): It was conceived as an extremely simple, portable tool to do a few basic formattings. I get that. But then, presumably because some needs arose, all sorts of things got bolted on, leading to mmd. The fine line of what features should be included and what not is sometimes hard to rationalize: Either stick to the simple guns and say “no images, period”, or allow simple image scaling commands (it’s almost as if they were inspired by NextStep’s RTFD). Drawing the KISS line there is strange. Same with the fact that bold face and italics are apparently important, but not font colour or size. I use md extensively in Jupyter notebooks, where rich documentation of computations is key for me. So I ended up writing all sorts of TextExpander shortcuts to quickly toss html-constructs into the md sections to get done what I need. I also use PmWiki extensively for my work, and it shows how very simple basic commands (essentially identical to Markdown) can be combined with ever increasing complexity for those who can/want wield it. No downside to that approach as far as I see.


I think it was conceived as an extremely simple tool to produce HTML without having to write it. That’s the reason why images are handled exactly as they’re in HTML: as external entities that the documents link to.

Whatever came later arose from the fact that people wanted to use md for purposes it was not meant for. Like „formatting“.

That’s just markdownish for the HTML elements strong and emphasize. Color and font size are handled by CSS, as well they should be.

Markdown was not about presentation but about semantics. Just like HTML. It seems that this origin is forgotten more and more. Which leads to all sorts of „improvements“ and „solutions“ to perceived problems. But unfortunately not to the decision to move to another format that does what one wants. Like include images and allow for easy font-color/size setting directly in the document.


@chrillek: I’m sure you’re right. I’m only vaguely familiar with the beginnings of Markdown. Wiki languages like PmWiki came along for a similar reason. The verboseness of html is in that sense very unfortunate. Same with TeX/LaTeX and Pascal. You can only deal with that many “begin{something}” (I write a lot of LaTeX). Nevermind, there are good reasons for doing it that way, so I won’t knock that (who am I to knock anything that Don Knuth has ever done).

Just as an example, PmWiki, which is also just a front end to rendering html, shows that it’s rather easy to go all the way. At the user level, it makes it entirely transparent whether something is a html element (strong/emphasize) or something coming through CSS. It just works. Now, while this has not been done to my knowledge, someone could write a PmWiki-markup standalone renderer for DT, Typora, etc., and it would function the same way as md, and it would be rather complete in its ability without having to resort to unbearably verbose html/css constructs inside your documents. I was under the impression that one basic tenet of these markup languages is that the un-rendered ascii text source is still human readable, which html and TeX are decidedly not. Markdown and PmWiki markup are, but Markdown with too many html constructs thrown in (like in my Jupyter notebooks) isn’t.

Inside DT and similar settings, md seems to have taken on a slightly different role, namely that of a portable, nimble format for (semi)rich documents, filling the remarkable void between plain text notes and full-fledged Word level stuff. For that purpose, it seems a bit underpowered, in my opinion, which no one has to take seriously :slight_smile: .

That’s one of the reasons I never got around to using (La)TeX – too much to type (that, and the penchant for american typographic conventions). Also, (La)TeX is kind of a hybrid language, since it deals with presentation and semantics at the same time.

Correct. HTML is readable (kind of), but it’s certainly not fun to do so. And, interestingly, Word, Pages etc. all use XML now under the hood – i.e. a human readable format (after you’ve unpacked the ZIP archive they use to hold everything together). Not that any human in their right mind would want to read that :crazy_face:

That is apparently the perception of many people. At least here – I haven’t seen the same kind of discussion in the context of Draft. In some enviroments, there seems to be a tendency to bend this format to perform something it was not meant to. Like the endless discussions about images: There are tons of formats that can handle embedded images just fine. MD is not one of them. It was not designed to be. In my mind, note taking is something quick and dirty (I’m not talking about the content here) – it’s a note, not a full fledged article. But it seems the borders between different forms of text are getting blurred.

1 Like