~30 Seconds Turns Tiny: Flarum's Litedown Subscript Trap
Flarum does not use CommonMark directly. It uses Litedown, whose syntax differs in a few places — the one that catches Thai users most often is ~, which we like to use for "approximately" but Litedown reads as a subscript opener.
I maintain a Flarum forum and ran into a problem posters kept complaining about — they would type "wait ~30 seconds" and 30 would turn into a tiny subscript hanging at the end of the line instead of normal text. At first I thought the theme was broken. It actually comes down to the syntax of the markdown parser Flarum uses, which Thai users often run into because of their typing habits, so I wanted to write it up.
Flarum Does Not Use CommonMark
Flarum renders with Litedown (part of the s9e/TextFormatter family), rather than the familiar CommonMark. They look similar but have some differences. The biggest troublemaker is ~.
Type ~30 and Litedown reads it as opening a subscript, then closes it implicitly — so 30 becomes a subscript. Thai users like to use ~ for "approximately" (around ~30 seconds, ~5 GB), so they run into this regularly.
How to Avoid It When Typing
The easiest way is to avoid ~ altogether when you mean "approximately":
- Use Thai words directly: "ราว 30 วินาที", "ประมาณ 5 GB"
- Use another symbol:
≈ 30 - If you really need
~, wrap it in inline code:`~30`will display ~30 literally.
Similarly, ^ in Litedown = superscript. You can use it to write x^2 with a superscript, but if that is not what you intend, wrap it in inline code too.
Fixing Old Posts Through the DB — Know That It Stores s9e XML
If you want to fix many old posts directly through the database, there is an important trap: Flarum does not store raw markdown in posts.content; it stores parsed s9e XML.
# ดูของจริงในคอลัมน์ content — จะเห็นเป็น XML ไม่ใช่ ~30
# <r><SUB>...</SUB></r> ประมาณนี้ ไม่ใช่ข้อความดิบ
A direct REPLACE needs to account for the XML structure, rather than searching for "~30", because that is no longer what it looks like in the database — after fixing it, remember to clear the cache so it renders again.
php flarum cache:clear
On some hosts, ModSecurity blocks the Flarum API's PATCH method — if you encounter this, send a POST with the X-HTTP-Method-Override: PATCH header instead. Also, hiding a post requires setting hidden_at in both the discussions and posts tables, not just one.
Summary
- Flarum uses Litedown, not CommonMark —
~= subscript,^= superscript - Thai users run into this often because they use ~ for "approximately" — avoid it with Thai words or inline code
- When fixing content through the DB, remember it stores s9e XML rather than raw markdown, then run cache:clear
- ModSec blocks PATCH -> use X-HTTP-Method-Override; to hide a post, set hidden_at in 2 tables
This is a small issue, but it comes up a lot, especially on forums where people write a lot of Thai. Once you know that ~ has a special meaning, it is easy to avoid. And if you need to fix a large number of old posts, remember that the database contains XML, not what you typed. If you maintain a Flarum forum with a lot of Thai content, keep this handy to save yourself some confusion. If I have left anything out or made a typo, I will come back and fix it later.
Want us to set this up?
Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.
Hit something like this? Stuck somewhere?
Come trade notes on the LinuxThai board



Comments 0
No comments yet — be the first