§ How to · with Picoo

How to customise chat in Roblox (TextChatService)

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

Most Roblox chat tutorials you will find describe a system that no longer runs.

TextChatService replaced the legacy Chat/ChatService module entirely, and almost everything you want — tags, commands, channels — is now a property or an instance rather than a forked module script.

TextChatService, not legacy

Built on the current API, so it works in new places without forking anything.

Rich name tags

Role and rank prefixes via TextChatMessage styling — no chat module fork.

Slash commands

TextChatCommand instances with server-side handling and validation.

Channels

Team, party or global channels players can switch between.

Filtering kept intact

Anything a player typed is filtered before it is shown to anyone else.

System messages

Announcements and join/leave notices in a distinct style.

Files Picoo ships for this prompt

2 files · 160 lines · ~25s · 1 credit

ChatSetup

Commands, channels, tag styling, system message helpers.

110 lines

ChatClient

Message decoration and channel switching UI.

50 lines

Sample output: ServerScriptService.ChatSetup

-- If a tutorial tells you to copy ChatModules out of Chat and edit them, it
-- predates TextChatService and will not work in a new place. Tags, commands and
-- channels are all first-class instances now.
local TextChatService = game:GetService("TextChatService")

-- Tags: decorate the message as it is rendered. Prefix data comes from the
-- server so a client cannot award itself [OWNER].
TextChatService.OnIncomingMessage = function(message: TextChatMessage)
	local props = Instance.new("TextChatMessageProperties")
	local src = message.TextSource
	if not src then return props end

	local plr = Players:GetPlayerByUserId(src.UserId)
	local tag = plr and plr:GetAttribute("ChatTag")     -- server-set attribute
	if tag then
		props.PrefixText = `<font color="#FFD166">[${tag}]</font> ${message.PrefixText}`
	end
	return props
end

-- Commands are instances, and the handler runs where you put the script.
-- Validate on the SERVER: a command is just a message a client can send.
local cmd = Instance.new("TextChatCommand")
cmd.Name = "HealCommand"
cmd.PrimaryAlias = "/heal"
cmd.Parent = TextChatService
cmd.Triggered:Connect(function(source: TextSource, _text: string)
	local plr = Players:GetPlayerByUserId(source.UserId)
	if not plr or not isAdmin(plr) then return end     -- never trust the alias
	local hum = plr.Character and plr.Character:FindFirstChildOfClass("Humanoid")
	if hum then hum.Health = hum.MaxHealth end
end)

How to customise chat in Roblox (TextChatService)

Chat is the one Roblox system where the majority of available tutorials are actively wrong, because the platform replaced it.

The old approach was to copy the ChatModules folder out of the Chat service into your place and edit Lua files that Roblox shipped. Every guide describing that flow — and there are years of them — now describes something that does not run. TextChatService is the current system, and the good news is that everything the fork used to be for is now a supported instance or callback.

Tags are the clearest example. Instead of editing a module to inject a prefix, you handle OnIncomingMessage and return properties with your own PrefixText, rich text and all. The important detail is where the rank comes from: read it from a server-set attribute or a server lookup, because if the client supplies its own tag, every player is one line of code away from a staff badge.

Commands follow the same shape. A TextChatCommand is an instance with an alias and a Triggered signal, which makes them pleasant to write and easy to misuse. The alias is not authentication — a command is simply a message the client decided to send, so the permission check belongs inside the handler on the server, exactly as it would for a remote.

Filtering is where the platform rules bite. Messages going through TextChatService are filtered for you. Text you take from a TextBox and render yourself — a pet name, a sign, a custom nameplate — is not, and showing it to other players unfiltered is against Roblox policy no matter how harmless the intent. Anything a player typed that another player will read goes through the filtering API first.

See more on the Luau generator, the game builder, or browse the full blog.

Frequently asked

Why doesn't my chat tutorial work any more?+

It is almost certainly written for the legacy Chat service, where customisation meant copying ChatModules into your place and editing them. TextChatService is the current system and works completely differently — tags come from OnIncomingMessage, commands are TextChatCommand instances, and channels are TextChannel instances. Any tutorial telling you to fork a chat module is out of date.

How do I add a rank or role tag?+

Handle TextChatService.OnIncomingMessage and return TextChatMessageProperties with a modified PrefixText. Get the rank from a server-set attribute or a server lookup, never from something the client supplies — otherwise anyone can hand themselves a staff tag.

Do I still need to filter text?+

Yes, for any player-authored text you display outside the chat window — a name on a sign, a pet name, a message on a billboard. Chat messages sent through TextChatService are filtered for you, but text you take from a TextBox and render yourself is not, and displaying it unfiltered violates Roblox's rules regardless of intent.

Are chat commands secure?+

A command is a message a client chose to send, so the alias proves nothing about who sent it. Check permissions inside the Triggered handler on the server, exactly as you would for a RemoteEvent.

Can I build my own chat UI?+

Yes — set TextChatService.ChatWindowConfiguration.Enabled to false and drive the messages yourself. You still send through TextChatService so filtering, moderation and channels keep working; you are replacing the window, not the pipeline.

Related Picoo prompts