Table of Contents
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Custom
Print a custom string, with or without key
| Module type | custom |
| Default order | — (no defaultOrder) |
| Module source | src/modules/custom/custom.c |
| Detection source | — |
Prints a literal string. It is the module you want for a static line, a separator-like label, or a value that comes from the environment:
hello
The default key is a single space, so nothing precedes the text. Give it a key and you get an
ordinary key: value line:
{ "modules": [ { "type": "custom", "key": "Note", "format": "hello" } ] }
Note: hello
Platform support
| Platform | Implementation | Notes |
|---|---|---|
| Everywhere | src/modules/custom/custom.c |
Platform independent |
No detection layer, no OS calls — the text is taken verbatim from the config.
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
format |
string | – | The text to print. This is where your text goes, not text. |
key |
string | " " |
Key text. A single space hides the key and its separator. |
keyColor |
color | – | Overrides display.color.keys. |
keyIcon |
string | built-in glyph | The icon printed when display.key.type includes the icon bit. Set it to any glyph you like, or to "" to print none. |
keyWidth |
integer | – | Overrides display.key.width. |
outputColor |
color | – | Overrides display.color.output. |
condition |
object | – | See Configuration. |
The keys above are the entire accepted set. text is not a valid key here — that is the
Command module's option.
Beware of the key default: because the initial key is a single space, a custom module without an
explicit key prints only its text. Writing "key": "" does not hide it either — see
Pitfalls.
Format string
custom publishes no format variables; fastfetch -h custom-format prints nothing.
The format string is still parsed as a format string, so it has access to the global syntax that
does not depend on module arguments:
| Syntax | Meaning |
|---|---|
{$NAME} |
Value of the environment variable NAME (empty if unset) |
{$1} … {$N} |
Constant from display.constants (1-based; negative counts from the end) |
{#RRGGBB}, {#name} |
Inline color escape |
{{ |
A literal { |
Note the $ in {$NAME}: {HOME} is not substituted and is printed literally as {HOME}.
{ "modules": [ { "type": "custom", "format": "HOME={$HOME} NOPE={$DOES_NOT_EXIST}" } ] }
HOME=/home/user NOPE={$DOES_NOT_EXIST}
An unset variable leaves the placeholder visible, which is a useful sanity check.
JSON output
The JSON result is the rendered format string — the same value the console prints:
{ "type": "Custom", "result": "hello" }
Placeholders are resolved first, so format: "HOME={$HOME}" exports the expanded text, and a
qjs: / lua: format exports the script's output, not the script itself.
A failure inside such a script is reported as error with no result:
{ "type": "Custom", "error": "Qjs runtime error: SyntaxError: expecting ';'" }
A plain format never fails, though: an unknown placeholder such as {bogus} is emitted verbatim
rather than treated as an error, so it lands in result unchanged.
Examples
A static label with a value that comes from the environment:
{
"modules": [
{ "type": "custom", "key": "Projects", "format": "{$HOME}/projects" }
]
}
A heading with inline color — note the single closing brace, since only {{ is an escape and a lone
} is literal:
{ "type": "custom", "format": "{#blue}{{ System }{#}" }
A blank line as spacing:
{ "type": "custom", "key": " ", "format": " " }
Constants are handy for repeated values:
{
"display": { "constants": ["/home/user/projects"] },
"modules": [ { "type": "custom", "key": "Projects", "format": "{$1}" } ]
}
Pitfalls
"key": ""shows the module name, it does not hide it. The runtime falls back toCustom: hellowhen the key is empty, so a single space is what hides the key and its separator. The sharedkeydefinition indoc/json_schema.jsonsays exactly that — “use a single space to hide the key” — andcustomnow reuses it rather than carrying its own wording.- The JSON
resultis the rendered text, not yourformatstring. Placeholders are already expanded and aqjs:/lua:script has already run, so the raw template is not recoverable from--format json; use--gen-configif you need the config form back.break,colorsandseparatorremain the only modules with no JSON result at all. formatis parsed, not printed verbatim. A{that is not part of a placeholder must be written{{, and any{$…}sequence is resolved. A Windows path such asC:\{$X}is a footgun; prefer forward slashes or escape the brace.- An
#-style color insideformatis emitted as\e[<color>mand relies on the following text to reset. Follow it with{#}or a named color to avoid bleeding into the next line. \eonly works in a.json5config..jsonand.jsoncare read with the strict reader, which rejects the escape withinvalid escaped sequence in string.{#name}/{#RRGGBB}(as used above) works in every dialect, as does\u001b. See Global.customhas no detection layer, so it never fails and never reports an error — a mistake informatis invisible. Test with--pipeto see the raw output.- Module options are JSON-only. There is no
--custom-formatflag any more.
Implementation
ffPrintCustom() is a two-line function:
bool ffPrintCustom(FFCustomOptions* options) {
ffPrintFormat(FF_MODULE_GET_DISPLAY_NAME(Custom), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, 0, ((FFformatarg[]) {}));
return true;
}
It delegates everything to ffPrintFormat() (src/common/impl/format.c) with zero format
arguments — that is why no {variable} resolves and why fastfetch -h custom-format has nothing to
list. What does still work is the argument-independent syntax: the {$…} branch of the parser reads
getenv() for a name and instance.config.display.constants for an index, neither of which depends
on the module's argument list.
ffInitCustomOptions() sets the icon and then ffStrbufSetStatic(&options->moduleArgs.key, " "), so
the key is a space rather than empty. FFCustomOptions holds nothing but moduleArgs
(src/modules/custom/option.h), so the whole configuration surface is the seven shared keys.
ffParseCustomJsonObject() accepts exactly what ffJsonConfigParseModuleArgs() understands and
reports anything else through ffPrintError(), which is silent unless display.showErrors is set.