Built-in Components Reference
Overview
go-tui ships with three built-in components: Input (single-line text), TextArea (multi-line text), and Modal (overlay dialog). Input and TextArea handle text entry, cursor management, and focus. Modal handles backdrop rendering, focus trapping, and preemptive key blocking.
All three implement Component, KeyListener, and AppBinder. Use them standalone or embed them in a larger UI.
As GSX elements (<input>, <textarea>, <modal>), they mount against the component's receiver, so place them inside a struct method component rather than a pure templ function. The same applies to <markdown>.
Input
A single-line text input with cursor management, horizontal scrolling, and placeholder support.
import tui "github.com/grindlemire/go-tui"
inp := tui.NewInput(
tui.WithInputWidth(30),
tui.WithInputBorder(tui.BorderRounded),
tui.WithInputPlaceholder("Type here..."),
tui.WithInputOnSubmit(func(text string) {
// handle submitted text
}),
)
NewInput
func NewInput(opts ...InputOption) *Input
Creates a new Input with the given options. Default values:
| Setting | Default | Description |
|---|---|---|
| Width | 20 | Characters visible before scrolling |
| Border | BorderNone |
No border |
| TextStyle | Style{} |
Default terminal style |
| Placeholder | "" |
No placeholder text |
| PlaceholderStyle | Style{}.Dim() |
Dim text for placeholder |
| Cursor | real cursor | Framework places the terminal cursor; WithInputVirtualCursor draws '▌' instead |
| FocusColor | Cyan |
Border color when focused |
Reactive Value Binding
Bind the Input to a *State[string] for two-way binding. The Input shares the state directly, so typing updates the state and changing the state updates the display:
name := tui.NewState("")
inp := tui.NewInput(
tui.WithInputValue(name),
tui.WithInputBorder(tui.BorderRounded),
)
// Later: name.Set("Alice") updates the input display
// Typing in the input updates name.Get()
In .gsx:
<input value={s.name} placeholder="Type your name..." border={tui.BorderRounded} />
GSX Attributes
All <input> attributes and their types:
| Attribute | Type | Description |
|---|---|---|
value |
*State[string] |
Two-way text binding |
placeholder |
string |
Text shown when empty and unfocused |
placeholderStyle |
tui.Style |
Placeholder styling (default: dim) |
width |
int |
Width in characters (default 20) |
border |
tui.BorderStyle |
Border style |
textStyle |
tui.Style |
Text styling |
cursor |
rune |
Cursor character (default '▌') |
focusColor |
tui.Color |
Border color when focused (default Cyan) |
borderGradient |
tui.Gradient |
Border gradient when unfocused |
focusGradient |
tui.Gradient |
Border gradient when focused |
onSubmit |
func(string) |
Called when Enter is pressed |
onChange |
func(string) |
Called when text changes |
autoFocus |
bool |
Focus this input on startup |
Focus Border Styling
Control how the border looks when focused and unfocused:
// Solid color when focused (default: Cyan)
tui.WithInputFocusColor(tui.Magenta)
// Gradient border when unfocused
tui.WithInputBorderGradient(tui.NewGradient(tui.Blue, tui.Cyan))
// Gradient border when focused (overrides focusColor)
tui.WithInputFocusGradient(tui.NewGradient(tui.Cyan, tui.Magenta))
In .gsx:
<input
value={s.query}
border={tui.BorderRounded}
focusColor={tui.Magenta}
borderGradient={tui.NewGradient(tui.Blue, tui.Cyan)}
focusGradient={tui.NewGradient(tui.Cyan, tui.Magenta)}
/>
Keyboard Behavior
Text input:
| Key | Action |
|---|---|
| Any rune | Insert character at cursor |
| Backspace | Delete character before cursor |
| Delete | Delete character at cursor |
Navigation:
| Key | Action |
|---|---|
| Left | Move cursor left |
| Right | Move cursor right |
| Home | Move cursor to start |
| End | Move cursor to end |
Submit:
| Key | Action |
|---|---|
| Enter | Trigger onSubmit callback |
Programmatic Editing
Move the cursor and insert text from code, for example to seed a value or build an autocomplete.
func (inp *Input) CursorPos() int
func (inp *Input) SetCursorPos(pos int)
func (inp *Input) InsertText(s string)
CursorPos returns the cursor position as a grapheme-cluster index, the count of whole glyphs before the cursor. That is the unit SetCursorPos accepts, so it is not a byte or rune offset and will not slice the text correctly if used that way. SetCursorPos clamps to [0, ClusterCount(text)] and lands on a cluster boundary. InsertText inserts at the cursor and advances past the inserted text, routing through the same path as typing so combining marks join the preceding glyph.
inp.SetCursorPos(0)
inp.InsertText("> ") // prefix the line, cursor ends after the prefix
InputOption Functions
| Function | Description |
|---|---|
WithInputWidth(int) |
Width in characters (default 20) |
WithInputBorder(BorderStyle) |
Border style |
WithInputTextStyle(Style) |
Text style |
WithInputPlaceholder(string) |
Placeholder text |
WithInputPlaceholderStyle(Style) |
Placeholder style (default: dim) |
WithInputCursor(rune) |
Cursor character (default '▌') |
WithInputValue(*State[string]) |
Reactive two-way text binding |
WithInputFocusColor(Color) |
Border color when focused (default Cyan) |
WithInputBorderGradient(Gradient) |
Border gradient when unfocused |
WithInputFocusGradient(Gradient) |
Border gradient when focused |
WithInputOnSubmit(func(string)) |
Enter key callback |
WithInputOnChange(func(string)) |
Text change callback |
WithInputVirtualCursor() |
Draw the ▌ glyph instead of using the real terminal cursor |
WithInputCursorRune(rune) |
Glyph used in virtual-cursor mode (default ▌) |
By default the Input drives the real terminal cursor and draws no glyph. Pass WithInputVirtualCursor() to paint the block glyph instead, and WithInputCursorRune to change it. WithInputCursor still works as a deprecated alias for WithInputCursorRune.
TextArea
A multi-line text input with word wrapping and a blinking cursor.
import tui "github.com/grindlemire/go-tui"
ta := tui.NewTextArea(
tui.WithTextAreaWidth(60),
tui.WithTextAreaBorder(tui.BorderRounded),
tui.WithTextAreaPlaceholder("Type something..."),
tui.WithTextAreaOnSubmit(func(text string) {
// handle submitted text
}),
)
NewTextArea
func NewTextArea(opts ...TextAreaOption) *TextArea
Creates a new TextArea with the given options. Default values:
| Setting | Default | Description |
|---|---|---|
| Width | 40 | Characters per line before wrapping |
| MaxHeight | 0 (no limit) | Maximum rows of text visible |
| Border | BorderNone |
No border |
| TextStyle | Style{} |
Default terminal style |
| Placeholder | "" |
No placeholder text |
| PlaceholderStyle | Style{}.Dim() |
Dim text for placeholder |
| Cursor | real cursor | Framework places the terminal cursor; WithTextAreaVirtualCursor draws '▌' instead |
| FocusColor | Cyan |
Border color when focused |
| SubmitKey | KeyEnter |
Enter submits, Ctrl+J inserts newline |
GSX Attributes
All <textarea> attributes and their types:
| Attribute | Type | Description |
|---|---|---|
value |
*State[string] |
Two-way text binding |
placeholder |
string |
Text shown when empty and unfocused |
placeholderStyle |
tui.Style |
Placeholder styling (default: dim) |
width |
int |
Width in characters (default 40) |
maxHeight |
int |
Maximum visible rows (0 = unlimited) |
border |
tui.BorderStyle |
Border style |
textStyle |
tui.Style |
Text styling |
cursor |
rune |
Cursor character (default '▌') |
focusColor |
tui.Color |
Border color when focused (default Cyan) |
borderGradient |
tui.Gradient |
Border gradient when unfocused |
focusGradient |
tui.Gradient |
Border gradient when focused |
submitKey |
tui.Key |
Key that triggers submit (default KeyEnter) |
onSubmit |
func(string) |
Called when submit key is pressed |
autoFocus |
bool |
Focus this text area on startup |
State Access Methods
Text
func (t *TextArea) Text() string
Returns the current text content.
SetText
func (t *TextArea) SetText(s string)
Replaces the text and moves the cursor to the end.
ta.SetText("Hello, world!")
fmt.Println(ta.Text()) // "Hello, world!"
Clear
func (t *TextArea) Clear()
Removes all text and resets the cursor to position 0.
Height
func (t *TextArea) Height() int
Returns the total rendered height in rows, including border rows if a border is set. The height depends on the current text content and word wrapping. If maxHeight is set, the returned value is capped to that limit (plus border rows).
Programmatic Editing
func (t *TextArea) CursorPos() int
func (t *TextArea) SetCursorPos(pos int)
func (t *TextArea) InsertText(s string)
CursorPos returns the cursor position as a grapheme-cluster index (whole glyphs before the cursor), which is the unit SetCursorPos accepts. It is not a byte or rune offset. SetCursorPos clamps to [0, ClusterCount(text)] and lands on a cluster boundary. InsertText inserts at the cursor and advances past it, using the same path as typing so the bound text and cursor stay cluster-consistent.
ta.InsertText("\n- ") // start a new bullet at the cursor
Focus Methods
TextArea implements the Focusable interface. When focused, the cursor blinks and keystrokes are captured. When unfocused, placeholder text appears (if configured) and no keystrokes are processed.
IsFocusable
func (t *TextArea) IsFocusable() bool
Always returns true.
Focus
func (t *TextArea) Focus()
Activates the text area. The cursor becomes visible and starts blinking.
Blur
func (t *TextArea) Blur()
Deactivates the text area. The cursor disappears and placeholder text shows if the input is empty.
HandleEvent
func (t *TextArea) HandleEvent(e Event) bool
Processes a keyboard event against the TextArea's key map. Returns true if the event was handled (and propagation should stop), false otherwise. This method is part of the Focusable interface and is called automatically by the focus manager when the TextArea has focus.
BindApp
func (t *TextArea) BindApp(app *App)
Binds the TextArea's internal reactive states to the given App, so that state changes trigger re-renders. Called automatically when the TextArea is used as a root component or mounted as a sub-component.
Keyboard Behavior
TextArea returns a KeyMap with built-in bindings. All bindings use stop propagation so keystrokes don't bubble up to parent components.
Text input:
| Key | Action |
|---|---|
| Any rune | Insert character at cursor |
| Backspace | Delete character before cursor |
| Delete | Delete character at cursor |
Navigation:
| Key | Action |
|---|---|
| Left | Move cursor left |
| Right | Move cursor right |
| Up | Move cursor up one line |
| Down | Move cursor down one line |
| Home | Move cursor to start of line |
| End | Move cursor to end of line |
Submit and newline (default submit key = Enter):
| Key | Action |
|---|---|
| Enter | Trigger onSubmit callback |
| Ctrl+J | Insert newline character |
When submitKey is set to something other than KeyEnter, the behavior flips: Enter inserts a newline and the configured key triggers submit.
Cursor
By default the TextArea drives the real terminal cursor and draws no glyph, so the terminal handles the blink. Pass WithTextAreaVirtualCursor() to paint the ▌ glyph in the buffer instead, and WithTextAreaCursorRune to change it.
In virtual-cursor mode, TextArea implements WatcherProvider and returns a timer watcher that toggles the glyph every 500ms while focused. The glyph resets to visible on every keystroke so the user always sees where they are typing.
TextAreaOption Functions
Options follow the functional options pattern. Each returns a TextAreaOption (which is func(*TextArea)).
WithTextAreaWidth
func WithTextAreaWidth(cells int) TextAreaOption
Sets the width in characters. Text wraps at this boundary. Default: 40.
ta := tui.NewTextArea(tui.WithTextAreaWidth(80))
WithTextAreaMaxHeight
func WithTextAreaMaxHeight(rows int) TextAreaOption
Caps the visible height to rows lines of text. Set to 0 (the default) for unlimited height. This does not include border rows. If a border is set, the actual element height is maxHeight + 2.
ta := tui.NewTextArea(tui.WithTextAreaMaxHeight(10))
WithTextAreaBorder
func WithTextAreaBorder(b BorderStyle) TextAreaOption
Sets the border style around the text area. Default: BorderNone.
ta := tui.NewTextArea(tui.WithTextAreaBorder(tui.BorderRounded))
WithTextAreaTextStyle
func WithTextAreaTextStyle(s Style) TextAreaOption
Sets the style for the text content. Default: zero-value Style{} (terminal default).
ta := tui.NewTextArea(
tui.WithTextAreaTextStyle(tui.NewStyle().Foreground(tui.ANSIColor(tui.Cyan))),
)
WithTextAreaPlaceholder
func WithTextAreaPlaceholder(text string) TextAreaOption
Sets placeholder text shown when the TextArea is empty and unfocused. Default: empty string (no placeholder).
ta := tui.NewTextArea(tui.WithTextAreaPlaceholder("Enter your message..."))
WithTextAreaPlaceholderStyle
func WithTextAreaPlaceholderStyle(s Style) TextAreaOption
Sets the style for placeholder text. Default: Style{}.Dim().
ta := tui.NewTextArea(
tui.WithTextAreaPlaceholderStyle(tui.NewStyle().Dim().Italic()),
)
WithTextAreaCursorRune
func WithTextAreaCursorRune(r rune) TextAreaOption
Sets the glyph drawn in virtual-cursor mode. Default: '▌' (left half block). It takes effect only with WithTextAreaVirtualCursor, since the default real-cursor mode draws no glyph. WithTextAreaCursor remains as a deprecated alias.
ta := tui.NewTextArea(
tui.WithTextAreaVirtualCursor(),
tui.WithTextAreaCursorRune('█'),
)
WithTextAreaSubmitKey
func WithTextAreaSubmitKey(k Key) TextAreaOption
Sets which key triggers the onSubmit callback. Default: KeyEnter.
When the submit key is KeyEnter, pressing Enter triggers submit and Ctrl+J inserts a newline. For any other submit key, Enter inserts a newline and the configured key triggers submit.
// Ctrl+S submits, Enter inserts newlines (good for multi-line editing)
ta := tui.NewTextArea(tui.WithTextAreaSubmitKey(tui.KeyCtrlS))
WithTextAreaOnSubmit
func WithTextAreaOnSubmit(fn func(string)) TextAreaOption
Sets the callback invoked when the submit key is pressed. The callback receives the current text content.
ta := tui.NewTextArea(
tui.WithTextAreaOnSubmit(func(text string) {
fmt.Println("Submitted:", text)
}),
)
WithTextAreaOnChange
func WithTextAreaOnChange(fn func(string)) TextAreaOption
Sets a callback invoked whenever the text content changes. The callback receives the full text after each change. It fires on user edits (typing, backspace, delete) and on programmatic changes through SetText, Clear, and InsertText. Use it to mirror the text elsewhere or to validate as the user types.
ta := tui.NewTextArea(
tui.WithTextAreaOnChange(func(text string) {
charCount.Set(len(text))
}),
)
This is a Go option. Unlike <input onChange>, the <textarea> tag does not yet expose onChange as an attribute, so set it through WithTextAreaOnChange when constructing the component.
WithTextAreaValue
func WithTextAreaValue(state *State[string]) TextAreaOption
Binds the TextArea to a *State[string] for two-way binding. The TextArea shares the state directly, so parent components can read or change the text at any time.
note := tui.NewState("")
ta := tui.NewTextArea(tui.WithTextAreaValue(note))
// note.Get() reflects whatever the user types
// note.Set("preset text") updates the textarea display
In .gsx:
<textarea value={s.note} placeholder="Write a note..." border={tui.BorderRounded} />
WithTextAreaFocusColor
func WithTextAreaFocusColor(c Color) TextAreaOption
Sets the border color when focused. Default: Cyan. Only visible when a border is set.
ta := tui.NewTextArea(
tui.WithTextAreaBorder(tui.BorderRounded),
tui.WithTextAreaFocusColor(tui.Magenta),
)
WithTextAreaBorderGradient
func WithTextAreaBorderGradient(g Gradient) TextAreaOption
Sets a gradient for the border color when unfocused. Only visible when a border is set.
ta := tui.NewTextArea(
tui.WithTextAreaBorder(tui.BorderRounded),
tui.WithTextAreaBorderGradient(tui.NewGradient(tui.Blue, tui.Cyan)),
)
WithTextAreaFocusGradient
func WithTextAreaFocusGradient(g Gradient) TextAreaOption
Sets a gradient for the border color when focused. Takes priority over focusColor when set.
ta := tui.NewTextArea(
tui.WithTextAreaBorder(tui.BorderRounded),
tui.WithTextAreaFocusGradient(tui.NewGradient(tui.Cyan, tui.Magenta)),
)
WithTextAreaVirtualCursor
func WithTextAreaVirtualCursor() TextAreaOption
Switches to the drawn ▌ glyph instead of the real terminal cursor. The glyph is customizable with WithTextAreaCursorRune.
Example
A note-taking input using TextArea with a rounded border and Ctrl+S to submit:
package main
import (
"fmt"
"os"
tui "github.com/grindlemire/go-tui"
)
func main() {
ta := tui.NewTextArea(
tui.WithTextAreaWidth(60),
tui.WithTextAreaMaxHeight(10),
tui.WithTextAreaBorder(tui.BorderRounded),
tui.WithTextAreaPlaceholder("Write a note..."),
tui.WithTextAreaSubmitKey(tui.KeyCtrlS),
tui.WithTextAreaOnSubmit(func(text string) {
fmt.Printf("Saved: %s\n", text)
}),
)
app, err := tui.NewApp(
tui.WithRootComponent(ta),
)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
defer app.Close()
if err := app.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
}
For an inline chat pattern with PrintAbove, see the Inline Mode Guide.
Modal
Modal renders a full-screen overlay with a backdrop effect, focus trapping, and preemptive key handling.
Constructor
func NewModal(opts ...ModalOption) *Modal
ModalOption Functions
| Function | Description |
|---|---|
WithModalOpen(state *State[bool]) |
Bind visibility to a boolean state (required) |
WithModalBackdrop(b string) |
Backdrop style: "dim" (default), "blank", or "none" |
WithModalCloseOnEscape(v bool) |
Escape closes the modal (default true) |
WithModalCloseOnBackdropClick(v bool) |
Backdrop click closes the modal (default true) |
WithModalTrapFocus(v bool) |
Restrict Tab navigation to modal children and block unhandled keys from parent handlers (default true) |
WithModalKeyMap(km KeyMap) |
Custom key bindings for the modal; fire after built-in handlers but before the catch-all. Use OnPreemptStop; non-preemptive bindings (On, OnStop) are inert when trapFocus is true |
WithModalElementOptions(opts ...Option) |
Pass layout options to the overlay container (used by generated code for class attributes) |
GSX Attributes
All <modal> attributes and their types:
| Attribute | Type | Description |
|---|---|---|
open |
*State[bool] |
Controls visibility (required) |
backdrop |
string |
"dim" (default), "blank", or "none" |
closeOnEscape |
bool |
Escape closes the modal (default true) |
closeOnBackdropClick |
bool |
Backdrop click closes the modal (default true) |
trapFocus |
bool |
Tab/Shift+Tab restricted to modal children; also blocks unhandled keys from parents (default true) |
keyMap |
expression |
Custom KeyMap bindings for the modal |
class |
string |
Tailwind classes for positioning (e.g. "justify-center items-center") |
Behavior
When open, the modal:
- Applies the backdrop effect (dim, blank, or none) to the buffer before rendering the overlay
- Handles Enter by calling
Activate()on the focused element - Closes on Escape (if
closeOnEscapeis true) - Closes on backdrop click (if
closeOnBackdropClickis true) - Walks clicked elements up to find
onActivatecallbacks for mouse support
When trapFocus is true (the default):
- Tab/Shift+Tab only cycle through focusable children inside the modal
- A catch-all binding blocks unhandled keys from parent handlers
When trapFocus is false, Tab and unhandled keys propagate to parent components normally.
Custom key bindings provided via WithModalKeyMap (or the keyMap GSX attribute) fire after the built-in Escape/Tab/Enter handlers but before the catch-all. This lets you add hotkeys to a modal while still blocking everything else:
tui.WithModalKeyMap(tui.KeyMap{
tui.OnPreemptStop(tui.Rune('n'), func(ke tui.KeyEvent) { startNewGame() }),
tui.OnPreemptStop(tui.Rune('q'), func(ke tui.KeyEvent) { quit() }),
})
When closed, it returns a hidden placeholder element with no key bindings.
Interfaces Implemented
| Interface | Purpose |
|---|---|
Component |
Render(app *App) *Element returns the overlay element |
KeyListener |
KeyMap() returns Escape, Tab, Enter, custom, and catch-all bindings (catch-all only when trapFocus is true) |
MouseListener |
HandleMouse() handles backdrop click and onActivate delegation |
AppBinder |
BindApp() wires the open state to the app |
GSX Usage
<modal open={s.showDialog} class="justify-center items-center" backdrop="dim">
<div class="border-rounded p-2 flex-col gap-1 w-40">
<span class="font-bold">Title</span>
<button class="px-2 border-rounded focusable" onActivate={s.onConfirm}>OK</button>
</div>
</modal>
The class attribute on <modal> controls how the dialog is positioned within the full-screen overlay. Use justify-center items-center for a centered dialog or justify-end items-stretch for a bottom sheet.
Inline Mode
Modals are not supported in inline mode (WithInlineHeight). The overlay system requires a full-screen buffer for backdrop effects, centering, and mouse hit testing. Modal overlays registered while in inline mode are silently ignored.
To show a modal from an inline app, switch to the alternate screen first:
app.EnterAlternateScreen()
s.showDialog.Set(true)
// ... user interacts with modal ...
// on close:
app.ExitAlternateScreen()
See the Inline Mode Guide for the full pattern.
Markdown
Markdown renders a markdown string into the widget tree. It is a pure content renderer with no scroll state or key handling, so it is wrapped in a scrollable container for long documents. The parsed block tree is cached and re-parsed only when the resolved source string changes.
Constructor
func NewMarkdown(opts ...MarkdownOption) *Markdown
MarkdownOption Functions
| Function | Description |
|---|---|
WithMarkdownSource(s string) |
Static markdown content. Ignored when a state source is set |
WithMarkdownState(s *State[string]) |
Reactive source; takes precedence over the static source and re-renders on change |
WithMarkdownWidth(w int) |
Fixed render width in characters. 0 (the default) fills the width the parent assigns |
WithMarkdownTheme(t MarkdownTheme) |
Override the default styling theme |
GSX Attributes
All <markdown> attributes and their types:
| Attribute | Type | Description |
|---|---|---|
source |
string |
Static markdown content (string expression) |
state |
*State[string] |
Reactive source; re-renders on change. Takes precedence over source |
width |
int |
Fixed render width in characters (0 fills the parent width) |
theme |
MarkdownTheme |
Override the default styling |
The tag is self-closing. Content comes from source or state rather than from children, because the generator cannot tell a literal markdown string from a Go expression that returns one.
Supported Markdown
| Construct | Notes |
|---|---|
| Headings | ATX (# through ######) and single-line setext (===, ---) |
| Emphasis | Bold, italic, and bold-italic, with */_ and **/__ markers |
| Inline code | Backtick spans |
| Links | Rendered as OSC 8 hyperlinks on capable terminals |
| Code blocks | Fenced blocks, syntax-highlighted for Go, JSON, Bash, and JS/TS |
| Tables | Pipe tables drawn as a full grid with inline formatting kept in cells |
| Lists | Ordered, unordered (-, *, +), and nested |
| Blockquotes | Including nested quotes and quotes that contain a list |
An emphasis delimiter with no closer stays literal, so see **docs and 3 * 4 render as written.
Width and Wrapping
With width at 0 (the default), the component fills the width its parent assigns. Paragraphs and headings wrap to that width, while list and blockquote content renders on one line and clips on overflow. Set an explicit width to wrap list and blockquote content as well.
Theming
MarkdownTheme is a flat struct of Style fields plus a few extras. DefaultMarkdownTheme() returns a glow-inspired theme. Override individual fields and pass the result to WithMarkdownTheme or the theme attribute.
| Field | Type | Controls |
|---|---|---|
Heading |
[6]Style |
Per-level heading styles, indexed 0 (h1) through 5 (h6) |
Paragraph |
Style |
Body text |
Bold, Italic, CodeSpan, Link |
Style |
Inline runs, layered over the surrounding text |
CodeBlockText |
Style |
Fenced code block text |
CodeBlockBg |
Color |
Code block fill (default: no fill) |
CodeBlockBorder |
BorderStyle |
Box border around code blocks |
CodeHighlighter |
CodeHighlighter |
Colorizes fenced code; nil disables highlighting |
TableHeader |
Style |
Table header cells |
TableBorder |
BorderStyle |
Table grid border |
BlockquoteBar |
rune |
Left bar glyph |
BlockquoteBarStyle |
Style |
Left bar style |
BlockquoteText |
Style |
Quoted text |
BulletMarker |
string |
Unordered-list marker, e.g. "• " |
Syntax Highlighting
Fenced code blocks run through the theme's CodeHighlighter. The default is a built-in zero-dependency lexer covering Go, JSON, Bash, and JS/TS. An unrecognized language renders in CodeBlockText.
type CodeHighlighter interface {
Highlight(lang, code string) [][]TextSpan
}
| Function | Description |
|---|---|
NewHighlighter(p Palette) |
Built-in highlighter using palette p |
DefaultPalette() |
Default One Dark color scheme as a Palette (a map[TokenKind]Color) |
Set theme.CodeHighlighter = nil to render code uncolored, pass NewHighlighter a custom Palette to recolor the built-in lexer, or implement CodeHighlighter to plug in another engine such as chroma.
Interfaces Implemented
| Interface | Purpose |
|---|---|
Component |
Render(app *App) *Element returns the rendered block tree |
AppBinder |
BindApp() wires the reactive state source to the app (a no-op for a static source) |
PropsUpdater |
UpdateProps() receives fresh source, width, and theme when re-rendered from cache |
GSX Usage
<div class="overflow-y-scroll scrollbar-hidden grow" ref={s.content} scrollOffset={0, s.scrollY.Get()}>
<markdown source={s.doc} width={80} />
</div>
Markdown holds no scroll position, so the surrounding container provides the ref, the scrollOffset binding, and the key handling. See the Markdown Guide for the full scrollable viewer.
Cross-References
- Component Interfaces Reference —
Component,KeyListener,WatcherProvider,Focusable,AppBinder - Events Reference —
KeyEvent,Keyconstants,KeyMap - State Reference —
State[T]used internally by TextArea - Styling Reference —
StyleandBorderStylefor visual configuration - Focus Guide — Focus management and Tab navigation
- Inline Mode Guide — Using TextArea in inline mode