A powerful, terminal-based JSON viewer library and CLI tool with vim-like navigation, built with Bubble Tea.
- π Library + CLI: Use as a standalone tool or embed in your Bubble Tea applications
- β¨οΈ Vim-like Navigation: hjkl keys, page up/down, home/end
- π Powerful Filtering: Text filters, JSONPath queries, content search
- π¨ Customizable Themes: Built-in themes (dark, light, monochrome) and full customization
- π Clipboard Support: Copy values, paths, or keys
- π§ Event System: Callbacks for selections, expansions, and user actions
- π± Embedded Mode: Perfect for integrating into larger applications
- β‘ High Performance: Efficiently handles large JSON files
go install github.com/johnnyfreeman/bonsai/cmd@latestgo get github.com/johnnyfreeman/bonsai/viewer# From file
bonsai example.json
# From stdin (pipe JSON data)
cat example.json | bonsai
curl -s api.example.com/data.json | bonsai
echo '{"key": "value"}' | bonsai
jq '.data' input.json | bonsaihjklor arrow keys: Navigate through the JSON treePgUp/Ctrl+U: Page up (half screen)PgDn/Ctrl+D: Page down (half screen)Home/g: Go to topEnd/G: Go to bottom
Enter/Space/l: Expand/collapse nodeh: Collapse current node or move to parentE: Expand all nodesC: Collapse all nodes
/: Enter text filter mode$: Enter JSONPath filter modes/Ctrl+F: Search in contentn: Next search matchN: Previous search match:/Ctrl+G: Goto path
c: Copy current valuep: Copy current pathy: Copy current key
r/Ctrl+R: Reset view (clear filters)?: Toggle helpq/Esc/Ctrl+C: Quit
package main
import (
"encoding/json"
"log"
tea "github.com/charmbracelet/bubbletea"
"github.com/johnnyfreeman/bonsai/viewer"
)
func main() {
// Parse your JSON data
var data interface{}
json.Unmarshal(jsonBytes, &data)
// Create viewer with default config
model := viewer.New(data)
// Run as a Bubble Tea program
tea.NewProgram(model, tea.WithAltScreen()).Run()
}config := viewer.DefaultConfig().
WithTheme(viewer.LightTheme()).
WithCallbacks(
func(node *viewer.Node) { /* on select */ },
func(node *viewer.Node) { /* on expand */ },
func(node *viewer.Node) { /* on collapse */ },
).
WithClipboard(func(content string) {
// Handle clipboard operations
}).
WithError(func(err error) {
// Handle errors
})
model := viewer.New(data, config)Perfect for integrating into larger applications:
config := viewer.DefaultConfig().
Embedded(). // Remove borders and help
WithSize(60, 20). // Fixed size
ReadOnly() // Disable clipboard
// Embed in your larger Bubble Tea model
type MyApp struct {
jsonViewer viewer.Model
// ... other components
}// Built-in themes
viewer.DefaultTheme() // Dark theme
viewer.LightTheme() // Light theme
viewer.MonochromeTheme() // Black and white
// Popular community themes
viewer.TokyoNightTheme() // Popular VS Code theme
viewer.CatppuccinMochaTheme() // Catppuccin dark variant
viewer.CatppuccinLatteTheme() // Catppuccin light variant
viewer.DraculaTheme() // Classic dark theme
viewer.NordTheme() // Arctic/minimal theme
viewer.GruvboxTheme() // Retro warm theme
// Or create your own
customTheme := viewer.Theme{
Header: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF6B6B")),
Key: lipgloss.NewStyle().Foreground(lipgloss.Color("#4ECDC4")),
// ... customize all elements
}config := viewer.DefaultConfig().
WithCallbacks(
// OnSelect - fired when cursor moves
func(node *viewer.Node) {
fmt.Printf("Selected: %s\n", node.Path)
},
// OnExpand - fired when node is expanded
func(node *viewer.Node) {
fmt.Printf("Expanded: %s\n", node.Path)
},
// OnCollapse - fired when node is collapsed
func(node *viewer.Node) {
fmt.Printf("Collapsed: %s\n", node.Path)
},
).
WithFilter(func(filter string) {
fmt.Printf("Filter applied: %s\n", filter)
}).
WithClipboard(func(content string) {
fmt.Printf("Copied: %s\n", content)
})// Main model
type Model struct { ... }
// Node represents a JSON node
type Node struct {
Key string
Value interface{}
Type NodeType
Children []*Node
Parent *Node
Expanded bool
Path string
}
// Configuration
type Config struct {
Theme Theme
ShowHelp bool
ShowBorders bool
EnableClipboard bool
// ... callbacks and options
}// Creation
viewer.New(data interface{}, config ...Config) Model
viewer.NewFromJSON([]byte, config ...Config) (Model, error)
viewer.NewFromReader(io.Reader, config ...Config) (Model, error)
// Querying
model.GetCurrentNode() *Node
model.GetFilteredData() interface{}
model.IsFiltered() bool
model.GetSearchMatches() []*Node
// Configuration
config.WithTheme(Theme) Config
config.WithSize(width, height int) Config
config.Embedded() Config
config.ReadOnly() ConfigCheck out the examples/ directory for complete examples:
- basic/: Simple standalone viewer
- embedded/: Embedding in a larger application
- custom-theme/: Custom styling and callbacks
Press $ to enter JSONPath mode with smart path suggestions:
Smart Path Suggestions: When you press $, the viewer intelligently suggests a JSONPath based on your current cursor position. For example, if you're on $.users[0].name, it will start with $.users[*].name to show all user names.
$.users[*].name- Get all user names$.config.database- Get database config$.items[?(@.active == true)]- Get active items$..email- Get all email fields recursively
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details.