Working With Roblox Rich Text When It Actually Works
Roblox richtext is a tag-based markup system that lets you style text inside UI elements without writing custom rendering code. It lives in properties like TextLabel, TextButton, and ChatService. You drop <color> or <font> tags into the string and Roblox parses it on the client. That is the short version. The long version involves more edge cases than anyone wants to admit. Most people learn <b>, <i>, <color>, <font>, and <size>. Those five cover about eighty percent of what you need. The color tag accepts either hex values like #FF0000 or named colors, though some names are inconsistently supported across older clients. The font tag takes Roblox font IDs, not arbitrary web fonts. You can stack these. <color=#00FFAA>hello</color> works fine. Put a color tag inside a font tag or vice versa and you are usually fine too, but the nesting order matters when you start mixing in deprecated attributes. Here is what actually trips people up: the <u> tag. Underline rendering is inconsistent between platforms. On PC it looks normal. On mobile it sometimes renders as a stray character or disappears entirely depending on the font size. Skip underline unless you have to. Same with strikethrough — it exists in the parser but the visual output is unreliable at certain sizes.
A Real Problem I Hit and How I Worked Around It
I was building a custom chat system for a project a while back and needed to embed player names with colors pulled from a data store. The names came in mixed case with special characters. Roblox rich text does not escape user input, so someone with a username containing a < character could break the markup entirely. Their input would terminate your color tag and inject raw text into the stream. I spent about two hours debugging what looked like random formatting bugs before realizing the source was unescaped characters in the username string. The fix was a simple sanitization function that ran before any rich text construction. It replaces < with <, > with >, and & with &. After that, the chat rendered correctly every time. Worth noting that Roblox does not auto-escape these in rich text the way it does with regular text properties. You have to do it yourself or accept the risk.
Things Beginners Miss About Roblox Rich Text
First, rich text parsing is strictly client-side. If you set text from the server without proper filtering, you are relying on the client to render your tags. This means exploiters can inject arbitrary rich text into any label you update from a remote event if you are not careful. Always validate or sanitize tag content on the server before firing anything down. Second, the <font> tag does not fall back gracefully. If you reference a font ID that does not exist on a player's device — which happens often with custom Roblox fonts that some clients cannot load — the entire TextLabel falls back to the default font. All your size and color tags still work, but the font tag silently fails. I learned this the hard way when testing on a low-end Android device and wondering why half my UI looked wrong. Third, rich text support in Roblox Chat is deprecated for the legacy system. If you are building a modern chat, use the newer ChatService APIs and keep rich text confined to your own UI labels. The built-in chat renderer has unpredictable behavior with tags, especially around message history and bubble positioning.
Get the Full Details

Practical Construction Pattern
When building rich text dynamically in Lua, I construct the string in a table and join it at the end. It is slightly more verbose than string concatenation but it prevents accidental tag misplacement and makes it easier to insert conditional formatting. Something like this: local parts = {}
table.insert(parts, "<color=#FF4444>")
table.insert(parts, sanitizedName)
table.insert(parts, "</color><b>")
table.insert(parts, message)
table.insert(parts, "</b>")
label.Text = table.concat(parts) This pattern scales well. When you need to add a font tag or a size change, you insert it at the right index instead of hunting through a concatenated string for the right spot. Takes maybe thirty seconds longer to write the first time and saves you from spending an hour debugging broken tag closures later.
Where Rich Text Completely Fails
There are contexts where rich text simply does not work regardless of what you do. GuiTextService operations that rely on precise measurement, like line wrapping calculations, do not account for rich text tags. If you are doing custom text wrapping or measuring string width for layout purposes, rich text tags will throw off your math. You have to strip the tags before calling MeasureString or getting the text bounds. Another hard failure point is rich text inside Roblox's UI ScrollingFrame with certain overflow modes. The rendering can clip text in unexpected ways when tags are near the edge of a scroll region. I encountered this with a leaderboard that had colored names and it would occasionally cut off the last character of wrapped lines. Switching to a custom drawing approach using ImageLabels and pre-rendered text sprites solved it, though that is significantly more work. If you need rich text in places where the standard parser cannot reach, the practical alternative is to render text to an image on the server or in a local module and display that instead. It is heavier on memory and less flexible, but it gives you full control over formatting without fighting the built-in parser.