When you have too much data for one screen, use the Paginator.

The paginator doesn't store your data. It just calculates slice bounds for you.
start and end indices to slice your data.package main
import (
"fmt"
"github.com/charmbracelet/bubbles/v2/paginator"
tea "github.com/charmbracelet/bubbletea/v2"
)
type model struct {
items []string
paginator paginator.Model
}
func newModel() model {
// 1. Create dummy items
var items []string
for i := 1; i < 101; i++ {
items = append(items, fmt.Sprintf("Item %d", i))
}
// 2. Setup paginator
p := paginator.New()
p.Type = paginator.Dots
p.PerPage = 5
p.SetTotalPages(len(items))
return model{
paginator: p,
items: items,
}
}
func (m model) Init() tea.Cmd {
return nil
}
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyPressMsg:
if msg.String() == "q" {
return m, tea.Quit
}
}
// 3. Update logic handles keypresses (h/l, arrows)
m.paginator, cmd = m.paginator.Update(msg)
return m, cmd
}
func (m model) View() string {
// 4. Get the slice bounds for the current page
start, end := m.paginator.GetSliceBounds(len(m.items))
s := "\n"
for _, item := range m.items[start:end] {
s += " • " + item + "\n"
}
s += "\n " + m.paginator.View() // Render dots
s += "\n\n ←/→ page • q: quit\n"
return s
}
func main() {
tea.NewProgram(newModel()).Run()
}
p.Type = paginator.Arabic // "Page 1 of 10"
p.Type = paginator.Dots // "• • •"