diff --git a/cmd/dynamo-browse/main.go b/cmd/dynamo-browse/main.go
index cafbe4f..76257cb 100644
--- a/cmd/dynamo-browse/main.go
+++ b/cmd/dynamo-browse/main.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"github.com/aws/aws-sdk-go-v2/config"
 	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
+	"github.com/charmbracelet/bubbles/key"
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/lmika/audax/internal/common/ui/commandctrl"
@@ -16,6 +17,7 @@ import (
 	"github.com/lmika/audax/internal/dynamo-browse/providers/dynamo"
 	"github.com/lmika/audax/internal/dynamo-browse/providers/workspacestore"
 	"github.com/lmika/audax/internal/dynamo-browse/services/itemrenderer"
+	"github.com/lmika/audax/internal/dynamo-browse/services/keybindings"
 	"github.com/lmika/audax/internal/dynamo-browse/services/tables"
 	workspaces_service "github.com/lmika/audax/internal/dynamo-browse/services/workspaces"
 	"github.com/lmika/audax/internal/dynamo-browse/ui"
@@ -80,8 +82,33 @@ func main() {
 	tableReadController := controllers.NewTableReadController(state, tableService, workspaceService, itemRendererService, *flagTable, true)
 	tableWriteController := controllers.NewTableWriteController(state, tableService, tableReadController)
 
+	defaultKeyBindings := &ui.KeyBindings{
+		View: &ui.ViewKeyBindings{
+			Mark:                 key.NewBinding(key.WithKeys("m"), key.WithHelp("m", "mark")),
+			CopyItemToClipboard:  key.NewBinding(key.WithKeys("c"), key.WithHelp("c", "copy item to clipboard")),
+			Rescan:               key.NewBinding(key.WithKeys("R"), key.WithHelp("R", "rescan")),
+			PromptForQuery:       key.NewBinding(key.WithKeys("?"), key.WithHelp("?", "prompt for query")),
+			PromptForFilter:      key.NewBinding(key.WithKeys("f"), key.WithHelp("/", "filter")),
+			ViewBack:             key.NewBinding(key.WithKeys("backspace"), key.WithHelp("backspace", "go back")),
+			ViewForward:          key.NewBinding(key.WithKeys("\\"), key.WithHelp("\\", "go forward")),
+			CycleLayoutForward:   key.NewBinding(key.WithKeys("w"), key.WithHelp("w", "cycle layout forward")),
+			CycleLayoutBackwards: key.NewBinding(key.WithKeys("W"), key.WithHelp("W", "cycle layout backward")),
+			PromptForCommand:     key.NewBinding(key.WithKeys(":"), key.WithHelp(":", "prompt for command")),
+			Quit:                 key.NewBinding(key.WithKeys("ctrl+c", "esc"), key.WithHelp("ctrl+c/esc", "quit")),
+		},
+	}
+
 	commandController := commandctrl.NewCommandController()
-	model := ui.NewModel(tableReadController, tableWriteController, itemRendererService, commandController)
+	keyBindingService := keybindings.NewService(defaultKeyBindings)
+	_ = keyBindingService
+
+	model := ui.NewModel(
+		tableReadController,
+		tableWriteController,
+		itemRendererService,
+		commandController,
+		defaultKeyBindings,
+	)
 
 	// Pre-determine if layout has dark background.  This prevents calls for creating a list to hang.
 	lipgloss.HasDarkBackground()
diff --git a/internal/dynamo-browse/controllers/events.go b/internal/dynamo-browse/controllers/events.go
index 9e1bbae..7896902 100644
--- a/internal/dynamo-browse/controllers/events.go
+++ b/internal/dynamo-browse/controllers/events.go
@@ -27,7 +27,7 @@ func (rs NewResultSet) ModeMessage() string {
 	}
 
 	if rs.currentFilter != "" {
-		modeLine = fmt.Sprintf("%v - Filter: '%v'", modeLine, rs.currentFilter)
+		modeLine = fmt.Sprintf("%v - PromptForFilter: '%v'", modeLine, rs.currentFilter)
 	}
 	return modeLine
 }
diff --git a/internal/dynamo-browse/services/keybindings/service.go b/internal/dynamo-browse/services/keybindings/service.go
new file mode 100644
index 0000000..7535f49
--- /dev/null
+++ b/internal/dynamo-browse/services/keybindings/service.go
@@ -0,0 +1,32 @@
+package keybindings
+
+import "reflect"
+
+type Service struct {
+	keyBindingValue reflect.Value
+}
+
+func NewService(keyBinding any) *Service {
+	v := reflect.ValueOf(keyBinding)
+	if v.Kind() != reflect.Pointer {
+		panic("keyBinding must be a pointer to a struct")
+	}
+
+	return &Service{
+		keyBindingValue: v.Elem(),
+	}
+}
+
+func (s *Service) Rebind(name string, key string) error {
+
+}
+
+func (s *Service) findFieldForBinding(name string) reflect.Value {
+
+}
+
+func (s *Service) findFieldForBindingInGroup(group reflect.Value, name string) reflect.Value {
+	for i := 0; i < group.NumField(); i++ {
+		group.Field(i).Type().
+	}
+}
diff --git a/internal/dynamo-browse/ui/keybindings.go b/internal/dynamo-browse/ui/keybindings.go
new file mode 100644
index 0000000..b56d454
--- /dev/null
+++ b/internal/dynamo-browse/ui/keybindings.go
@@ -0,0 +1,21 @@
+package ui
+
+import "github.com/charmbracelet/bubbles/key"
+
+type KeyBindings struct {
+	View *ViewKeyBindings `keymap:"view,group"`
+}
+
+type ViewKeyBindings struct {
+	Mark                 key.Binding `keymap:"mark"`
+	CopyItemToClipboard  key.Binding `keymap:"copy-item-to-clipboard"`
+	Rescan               key.Binding `keymap:"rescan"`
+	PromptForQuery       key.Binding `keymap:"prompt-for-query"`
+	PromptForFilter      key.Binding `keymap:"prompt-for-filter"`
+	ViewBack             key.Binding `keymap:"view-back"`
+	ViewForward          key.Binding `keymap:"view-forward"`
+	CycleLayoutForward   key.Binding `keymap:"cycle-layout-forward"`
+	CycleLayoutBackwards key.Binding `keymap:"cycle-layout-backwards"`
+	PromptForCommand     key.Binding `keymap:"prompt-for-command"`
+	Quit                 key.Binding `keymap:"quit"`
+}
diff --git a/internal/dynamo-browse/ui/model.go b/internal/dynamo-browse/ui/model.go
index c5584c3..c1c4177 100644
--- a/internal/dynamo-browse/ui/model.go
+++ b/internal/dynamo-browse/ui/model.go
@@ -1,6 +1,7 @@
 package ui
 
 import (
+	"github.com/charmbracelet/bubbles/key"
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/lmika/audax/internal/common/ui/commandctrl"
 	"github.com/lmika/audax/internal/common/ui/events"
@@ -45,6 +46,7 @@ type Model struct {
 	tableView *dynamotableview.Model
 	itemView  *dynamoitemview.Model
 	mainView  tea.Model
+	keyMap    *ViewKeyBindings
 }
 
 func NewModel(
@@ -52,6 +54,7 @@ func NewModel(
 	wc *controllers.TableWriteController,
 	itemRendererService *itemrenderer.Service,
 	cc *commandctrl.CommandController,
+	defaultKeyMap *KeyBindings,
 ) Model {
 	uiStyles := styles.DefaultStyles
 
@@ -126,6 +129,10 @@ func NewModel(
 				return wc.NoisyTouchItem(dtv.SelectedItemIndex())
 			},
 
+			//"rebind": func(args []string) tea.Msg {
+			//
+			//},
+
 			// Aliases
 			"sa": cc.Alias("set-attr"),
 			"da": cc.Alias("del-attr"),
@@ -147,6 +154,7 @@ func NewModel(
 		tableView:            dtv,
 		itemView:             div,
 		mainView:             mainView,
+		keyMap:               defaultKeyMap.View,
 	}
 }
 
@@ -163,40 +171,39 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		return m, m.tableView.Refresh()
 	case tea.KeyMsg:
 		if !m.statusAndPrompt.InPrompt() && !m.tableSelect.Visible() {
-			log.Printf("key = %+v", msg)
-			switch msg.String() {
-			case "m":
+			switch {
+			case key.Matches(msg, m.keyMap.Mark):
 				if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
 					return m, func() tea.Msg { return m.tableWriteController.ToggleMark(idx) }
 				}
-			case "c":
+			case key.Matches(msg, m.keyMap.CopyItemToClipboard):
 				if idx := m.tableView.SelectedItemIndex(); idx >= 0 {
 					return m, func() tea.Msg { return m.tableReadController.CopyItemToClipboard(idx) }
 				}
-			case "R":
+			case key.Matches(msg, m.keyMap.Rescan):
 				return m, m.tableReadController.Rescan
-			case "?":
+			case key.Matches(msg, m.keyMap.PromptForQuery):
 				return m, m.tableReadController.PromptForQuery
-			case "/":
+			case key.Matches(msg, m.keyMap.PromptForFilter):
 				return m, m.tableReadController.Filter
-			case "backspace":
+			case key.Matches(msg, m.keyMap.ViewBack):
 				return m, m.tableReadController.ViewBack
-			case "\\":
+			case key.Matches(msg, m.keyMap.ViewForward):
 				return m, m.tableReadController.ViewForward
-			case "w":
+			case key.Matches(msg, m.keyMap.CycleLayoutForward):
 				return m, func() tea.Msg {
 					return controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, 1, ViewModeCount)}
 				}
-			case "W":
+			case key.Matches(msg, m.keyMap.CycleLayoutBackwards):
 				return m, func() tea.Msg {
 					return controllers.SetTableItemView{ViewIndex: utils.Cycle(m.mainViewIndex, -1, ViewModeCount)}
 				}
 			//case "e":
 			//	m.itemEdit.Visible()
 			//	return m, nil
-			case ":":
+			case key.Matches(msg, m.keyMap.PromptForCommand):
 				return m, m.commandController.Prompt
-			case "ctrl+c", "esc":
+			case key.Matches(msg, m.keyMap.Quit):
 				return m, tea.Quit
 			}
 		}