diff --git a/src/wuttaweb/grids/base.py b/src/wuttaweb/grids/base.py
index a754a2c..fcfff93 100644
--- a/src/wuttaweb/grids/base.py
+++ b/src/wuttaweb/grids/base.py
@@ -119,6 +119,12 @@ class Grid:
        See also :meth:`set_renderer()` and
        :meth:`set_default_renderers()`.
 
+    .. attribute:: enums
+
+       Dict of "enum" collections, for supported columns.
+
+       See also :meth:`set_enum()`.
+
     .. attribute:: checkable
 
        Boolean indicating whether the grid should expose per-row
@@ -377,6 +383,7 @@ class Grid:
             data=None,
             labels={},
             renderers={},
+            enums={},
             checkable=False,
             row_class=None,
             actions=[],
@@ -458,6 +465,11 @@ class Grid:
             self.filters = {}
         self.set_filter_defaults(**(filter_defaults or {}))
 
+        # enums
+        self.enums = {}
+        for key in enums:
+            self.set_enum(key, enums[key])
+
     def get_columns(self):
         """
         Returns the official list of column names for the grid, or
@@ -720,6 +732,27 @@ class Grid:
                     elif isinstance(column.type, sa.Boolean):
                         self.set_renderer(key, self.render_boolean)
 
+    def set_enum(self, key, enum):
+        """
+        Set the "enum" collection for a given column.
+
+        This will set the column renderer to show the appropriate enum
+        value for each row in the grid.  See also
+        :meth:`render_enum()`.
+
+        If the grid has a corresponding filter for the column, it will
+        be modified to show "choices" for values contained in the
+        enum.
+
+        :param key: Name of column.
+
+        :param enum: Instance of :class:`python:enum.Enum`.
+        """
+        self.enums[key] = enum
+        self.set_renderer(key, self.render_enum, enum=enum)
+        if key in self.filters:
+            self.filters[key].set_choices(enum)
+
     def set_link(self, key, link=True):
         """
         Explicitly enable or disable auto-link behavior for a given
@@ -1945,6 +1978,33 @@ class Grid:
         dt = getattr(obj, key)
         return self.app.render_datetime(dt)
 
+    def render_enum(self, obj, key, value, enum=None):
+        """
+        Custom grid value renderer for "enum" fields.
+
+        See also :meth:`set_enum()`.
+
+        :param enum: Enum class for the field.  This should be an
+           instance of :class:`~python:enum.Enum`.
+
+        To use this feature for your grid::
+
+           from enum import Enum
+
+           class MyEnum(Enum):
+               ONE = 1
+               TWO = 2
+               THREE = 3
+
+           grid.set_enum('my_enum_field', MyEnum)
+        """
+        if enum:
+            raw_value = obj[key]
+            if raw_value:
+                return raw_value.value
+
+        return value
+
     def render_percent(self, obj, key, value, **kwargs):
         """
         Column renderer for percentage values.
@@ -2176,6 +2236,13 @@ class Grid:
         """
         filters = []
         for filtr in self.filters.values():
+
+            choices = []
+            choice_labels = {}
+            if filtr.choices:
+                choices = list(filtr.choices)
+                choice_labels = dict(filtr.choices)
+
             filters.append({
                 'key': filtr.key,
                 'data_type': filtr.data_type,
@@ -2185,6 +2252,8 @@ class Grid:
                 'verb_labels': filtr.get_verb_labels(),
                 'valueless_verbs': filtr.get_valueless_verbs(),
                 'verb': filtr.verb,
+                'choices': choices,
+                'choice_labels': choice_labels,
                 'value': filtr.value,
                 'label': filtr.label,
             })
diff --git a/src/wuttaweb/grids/filters.py b/src/wuttaweb/grids/filters.py
index 1250b3d..1ecee1c 100644
--- a/src/wuttaweb/grids/filters.py
+++ b/src/wuttaweb/grids/filters.py
@@ -2,7 +2,7 @@
 ################################################################################
 #
 #  wuttaweb -- Web App for Wutta Framework
-#  Copyright © 2024 Lance Edgar
+#  Copyright © 2024-2025 Lance Edgar
 #
 #  This file is part of Wutta Framework.
 #
@@ -26,6 +26,8 @@ Grid Filters
 
 import datetime
 import logging
+from collections import OrderedDict
+from enum import EnumType
 
 import sqlalchemy as sa
 
@@ -77,6 +79,7 @@ class GridFilter:
 
        * ``'string'``
        * ``'date'``
+       * ``'choice'``
 
        Note that this mainly applies to the "value input" used by the
        filter.  There is no data type for boolean since it does not
@@ -94,6 +97,13 @@ class GridFilter:
 
        See also :attr:`value`.
 
+    .. attribute:: choices
+
+       OrderedDict of possible values for the filter.
+
+       This is safe to read from, but use :meth:`set_choices()` to
+       update it.
+
     .. attribute:: value
 
        Value for current filter, if :attr:`active` is true.
@@ -159,6 +169,7 @@ class GridFilter:
             key,
             label=None,
             verbs=None,
+            choices={},
             default_active=False,
             default_verb=None,
             default_value=None,
@@ -180,6 +191,9 @@ class GridFilter:
         if default_verb:
             self.default_verb = default_verb
 
+        # choices
+        self.set_choices(choices)
+
         # value
         self.default_value = default_value
         self.value = self.default_value
@@ -255,6 +269,72 @@ class GridFilter:
 
         return verb
 
+    def set_choices(self, choices):
+        """
+        Set the value choices for the filter.
+
+        If ``choices`` is non-empty, it is passed to
+        :meth:`normalize_choices()` and the result is assigned to
+        :attr:`choices`.  Also, the :attr:`data_type` is set to
+        ``'choice'`` so the UI will present the value input as a
+        dropdown.
+
+        But if ``choices`` is empty, :attr:`choices` is set to an
+        empty dict, and :attr:`data_type` is set (back) to
+        ``'string'``.
+
+        :param choices: Collection of "choices" or ``None``.
+        """
+        if choices:
+            self.choices = self.normalize_choices(choices)
+            self.data_type = 'choice'
+        else:
+            self.choices = {}
+            self.data_type = 'string'
+
+    def normalize_choices(self, choices):
+        """
+        Normalize a collection of "choices" to standard ``OrderedDict``.
+
+        This is called automatically by :meth:`set_choices()`.
+
+        :param choices: A collection of "choices" in one of the following
+           formats:
+
+           * :class:`python:enum.Enum` class
+           * simple list, each value of which should be a string,
+             which is assumed to be able to serve as both key and
+             value (ordering of choices will be preserved)
+           * simple dict, keys and values of which will define the
+             choices (note that the final choices will be sorted by
+             key!)
+           * OrderedDict, keys and values of which will define the
+             choices (ordering of choices will be preserved)
+
+        :rtype: :class:`python:collections.OrderedDict`
+        """
+        normalized = choices
+
+        if isinstance(choices, EnumType):
+            normalized = OrderedDict([
+                (member.name, member.value)
+                for member in choices])
+
+        elif isinstance(choices, OrderedDict):
+            normalized = choices
+
+        elif isinstance(choices, dict):
+            normalized = OrderedDict([
+                (key, choices[key])
+                for key in sorted(choices)])
+
+        elif isinstance(choices, list):
+            normalized = OrderedDict([
+                (key, key)
+                for key in choices])
+
+        return normalized
+
     def apply_filter(self, data, verb=None, value=UNSPECIFIED):
         """
         Filter the given data set according to a verb/value pair.
diff --git a/src/wuttaweb/templates/wutta-components.mako b/src/wuttaweb/templates/wutta-components.mako
index 81cfcb3..59f7d91 100644
--- a/src/wuttaweb/templates/wutta-components.mako
+++ b/src/wuttaweb/templates/wutta-components.mako
@@ -486,6 +486,17 @@
                                  v-show="valuedVerb()"
                                  :is-small="isSmall" />
 
+        
+          
+        
+