Tutorials

Paginator

Easily split content into multiple pages.

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

How It Works

The paginator doesn't store your data. It just calculates slice bounds for you.

  1. Tell it how many items you have per page.
  2. Tell it the total number of items.
  3. Ask it for start and end indices to slice your data.

Code Example

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()
}

Types of Paginators

p.Type = paginator.Arabic // "Page 1 of 10"
p.Type = paginator.Dots   // "• • •"