Overriding with Tags
Every tag in LuauDocs is a correction, never a requirement: you reach for one when the code says something other than what you mean, or when inference genuinely cannot see what you want documented. A library with no tags at all still gets a complete site. The handful below cover nearly every real case; Reference: Tags has the complete vocabulary: 24 tags, exactly Moonwave's set.
Tags go inside a doc comment, and prose can sit before or after them.
Describing parameters and returns
The signature already carries the types, so these tags exist to describe the values, not to declare them:
--[[
Sends a request and waits for the response.
@param url -- where to send it
@param retries -- attempts after the first; defaults to 3
@return -- the decoded body
@error Timeout -- raised when the server does not answer in time
]]
function Http.get(url: string, retries: number?): BodyYou can write a type after the name (@param url string -- ...), and it replaces the annotation in the rendered signature when you do, but usually the annotation already says it. @return tags apply in order. If you misspell a parameter name or write more @return tags than the function returns values, LuauDocs emits a warning rather than rendering inaccurate documentation.
In a multi-line signature, no tag is needed at all: a trailing comment on a parameter's line documents that parameter, the same way it documents a property or a type field.
function Http.get(
url: string, -- where to send it
retries: number? -- attempts after the first; defaults to 3
): BodyAn @param naming the same parameter overrides its trailing comment field by field: a type-only @param retries number replaces the type and keeps the trailing description.
Moving a member somewhere else
Sometimes a member is defined in one place and belongs, from a reader's point of view, in another. @within files it under a different class wherever it is defined:
--[[
Formats a duration for display.
@within Timer
]]
local function formatDuration(seconds: number): string@class Name documents a block as a class, which you need when the class is assembled in a way inference cannot follow. The other placement tags do the same for a single member or type; Reference: Tags lists them in full.
Only one of these placement tags belongs in a block. A second one is a conflict: the first wins and the later one is dropped and reported. @within is a data tag rather than a placement tag, so it combines freely with any of them.
Hiding things
--[[ @ignore ]]
function M.experimentalThing() end@ignoredrops the item from the docs entirely, always.@privatemarks it private, which hides it by default but renders it (badged) when you set[api] includePrivate = true.
An explicit tag takes precedence over the leading-underscore convention, so @private hides a member whose name has no underscore, and the convention decides only for members you never tagged.
Adding badges
Badges annotate a member without changing what it is, and the common ones need no tag, because LuauDocs reads the body. A function that waits (task.wait, signal:Wait(), a yielding engine method, or a call to something that does, in this module or one it requires) is badged Yields. Code only one realm can run (firing a remote, a realm-only service, the plugin global, a Studio-only member) is badged Server, Client, or Plugin. A property renders Read Only when its table is table.freezed or its field is marked read in the table's own type; a read field in a type imported from another module is not detected, so @readonly still applies there.
The example library's Queue shows both halves: push is tagged @since, while pop carries no tag at all, its badge detected from the task.wait() in its body:
--[[
Adds `item` to the back of the queue.
@since 1.1.0
]]
function Queue:push(item: any)
table.insert(self.items, item)
end
--[[ Removes the front item and returns it, waiting when the queue is empty. ]]
function Queue:pop(): any
while #self.items == 0 do
task.wait()
end
return table.remove(self.items, 1)
endMethods
push since 1.1.0
Queue:push(item: any)Adds item to the back of the queue.
pop Yields
Queue:pop(): anyRemoves the front item and returns it, waiting when the queue is empty.
These tags remain available when runtime behavior cannot be detected statically: @yields when yielding occurs within a callback passed as a value, and @server, @client, or @plugin when realm boundaries are conventions rather than calls. A realm tag replaces the detected realm entirely, so the tag wins when they disagree. @unreleased and @since track versioning, and @tag name badges anything else you want to call out. Badges render in a fixed order no matter which order you wrote them in.
@deprecated is the one that does more than badge. Writing @deprecated 2.0 -- states are collected once nothing references them on the example's State:Destroy badged the heading and added a callout under it:
Destroy Deprecated since 2.0
DEPRECATED
states are collected once nothing references them
State:Destroy()Drops every listener and releases the state.
The rest
The full vocabulary, @external and @field included, is cataloged in Reference: Tags, along with how a -- desc continues over several lines.