diff --git a/esphome/components/lvgl/__init__.py b/esphome/components/lvgl/__init__.py
index c64ffcb5f2..07f5d94543 100644
--- a/esphome/components/lvgl/__init__.py
+++ b/esphome/components/lvgl/__init__.py
@@ -61,7 +61,14 @@ from .types import (
     lv_style_t,
     lvgl_ns,
 )
-from .widgets import Widget, add_widgets, get_scr_act, set_obj_properties, styles_used
+from .widgets import (
+    LvScrActType,
+    Widget,
+    add_widgets,
+    get_scr_act,
+    set_obj_properties,
+    styles_used,
+)
 from .widgets.animimg import animimg_spec
 from .widgets.arc import arc_spec
 from .widgets.button import button_spec
@@ -318,7 +325,7 @@ async def to_code(configs):
             config[df.CONF_RESUME_ON_INPUT],
         )
         await cg.register_component(lv_component, config)
-        Widget.create(config[CONF_ID], lv_component, obj_spec, config)
+        Widget.create(config[CONF_ID], lv_component, LvScrActType(), config)
 
         lv_scr_act = get_scr_act(lv_component)
         async with LvContext():
@@ -391,7 +398,7 @@ FINAL_VALIDATE_SCHEMA = final_validation
 
 LVGL_SCHEMA = (
     cv.polling_component_schema("1s")
-    .extend(obj_schema(obj_spec))
+    .extend(obj_schema(LvScrActType()))
     .extend(
         {
             cv.GenerateID(CONF_ID): cv.declare_id(LvglComponent),
diff --git a/esphome/components/lvgl/defines.py b/esphome/components/lvgl/defines.py
index 733a6bc180..119a358e1d 100644
--- a/esphome/components/lvgl/defines.py
+++ b/esphome/components/lvgl/defines.py
@@ -146,6 +146,8 @@ TYPE_FLEX = "flex"
 TYPE_GRID = "grid"
 TYPE_NONE = "none"
 
+DIRECTIONS = LvConstant("LV_DIR_", "LEFT", "RIGHT", "BOTTOM", "TOP")
+
 LV_FONTS = list(f"montserrat_{s}" for s in range(8, 50, 2)) + [
     "dejavu_16_persian_hebrew",
     "simsun_16_cjk",
@@ -169,9 +171,13 @@ LV_EVENT_MAP = {
     "CANCEL": "CANCEL",
     "ALL_EVENTS": "ALL",
     "CHANGE": "VALUE_CHANGED",
+    "GESTURE": "GESTURE",
 }
 
 LV_EVENT_TRIGGERS = tuple(f"on_{x.lower()}" for x in LV_EVENT_MAP)
+SWIPE_TRIGGERS = tuple(
+    f"on_swipe_{x.lower()}" for x in DIRECTIONS.choices + ("up", "down")
+)
 
 
 LV_ANIM = LvConstant(
@@ -250,7 +256,6 @@ KEYBOARD_MODES = LvConstant(
     "NUMBER",
 )
 ROLLER_MODES = LvConstant("LV_ROLLER_MODE_", "NORMAL", "INFINITE")
-DIRECTIONS = LvConstant("LV_DIR_", "LEFT", "RIGHT", "BOTTOM", "TOP")
 TILE_DIRECTIONS = DIRECTIONS.extend("HOR", "VER", "ALL")
 CHILD_ALIGNMENTS = LvConstant(
     "LV_ALIGN_",
diff --git a/esphome/components/lvgl/schemas.py b/esphome/components/lvgl/schemas.py
index f0318dd17a..ae50d5b2e1 100644
--- a/esphome/components/lvgl/schemas.py
+++ b/esphome/components/lvgl/schemas.py
@@ -211,10 +211,9 @@ def part_schema(parts):
 
 
 def automation_schema(typ: LvType):
+    events = df.LV_EVENT_TRIGGERS + df.SWIPE_TRIGGERS
     if typ.has_on_value:
-        events = df.LV_EVENT_TRIGGERS + (CONF_ON_VALUE,)
-    else:
-        events = df.LV_EVENT_TRIGGERS
+        events = events + (CONF_ON_VALUE,)
     args = typ.get_arg_type() if isinstance(typ, LvType) else []
     args.append(lv_event_t_ptr)
     return {
diff --git a/esphome/components/lvgl/trigger.py b/esphome/components/lvgl/trigger.py
index fb856df04e..b76f90fecd 100644
--- a/esphome/components/lvgl/trigger.py
+++ b/esphome/components/lvgl/trigger.py
@@ -7,8 +7,10 @@ from .defines import (
     CONF_ALIGN_TO,
     CONF_X,
     CONF_Y,
+    DIRECTIONS,
     LV_EVENT_MAP,
     LV_EVENT_TRIGGERS,
+    SWIPE_TRIGGERS,
     literal,
 )
 from .lvcode import (
@@ -23,7 +25,7 @@ from .lvcode import (
     lvgl_static,
 )
 from .types import LV_EVENT
-from .widgets import widget_map
+from .widgets import LvScrActType, get_scr_act, widget_map
 
 
 async def generate_triggers():
@@ -33,6 +35,9 @@ async def generate_triggers():
     """
 
     for w in widget_map.values():
+        if isinstance(w.type, LvScrActType):
+            w = get_scr_act(w.var)
+
         if w.config:
             for event, conf in {
                 event: conf
@@ -43,6 +48,24 @@ async def generate_triggers():
                 w.add_flag("LV_OBJ_FLAG_CLICKABLE")
                 event = literal("LV_EVENT_" + LV_EVENT_MAP[event[3:].upper()])
                 await add_trigger(conf, w, event)
+
+            for event, conf in {
+                event: conf
+                for event, conf in w.config.items()
+                if event in SWIPE_TRIGGERS
+            }.items():
+                conf = conf[0]
+                dir = event[9:].upper()
+                dir = {"UP": "TOP", "DOWN": "BOTTOM"}.get(dir, dir)
+                dir = DIRECTIONS.mapper(dir)
+                w.clear_flag("LV_OBJ_FLAG_SCROLLABLE")
+                selected = literal(
+                    f"lv_indev_get_gesture_dir(lv_indev_get_act()) == {dir}"
+                )
+                await add_trigger(
+                    conf, w, literal("LV_EVENT_GESTURE"), is_selected=selected
+                )
+
             for conf in w.config.get(CONF_ON_VALUE, ()):
                 await add_trigger(
                     conf,
@@ -61,13 +84,14 @@ async def generate_triggers():
                 lv.obj_align_to(w.obj, target, align, x, y)
 
 
-async def add_trigger(conf, w, *events):
+async def add_trigger(conf, w, *events, is_selected=None):
+    is_selected = is_selected or w.is_selected()
     tid = conf[CONF_TRIGGER_ID]
     trigger = cg.new_Pvariable(tid)
     args = w.get_args() + [(lv_event_t_ptr, "event")]
     value = w.get_values()
     await automation.build_automation(trigger, args, conf)
     async with LambdaContext(EVENT_ARG, where=tid) as context:
-        with LvConditional(w.is_selected()):
+        with LvConditional(is_selected):
             lv_add(trigger.trigger(*value, literal("event")))
     lv_add(lvgl_static.add_event_cb(w.obj, await context.get_lambda(), *events))
diff --git a/tests/components/lvgl/lvgl-package.yaml b/tests/components/lvgl/lvgl-package.yaml
index c51a3d03e7..c527f51b1e 100644
--- a/tests/components/lvgl/lvgl-package.yaml
+++ b/tests/components/lvgl/lvgl-package.yaml
@@ -133,6 +133,18 @@ lvgl:
 
   pages:
     - id: page1
+      on_swipe_top:
+        logger.log: "swiped up"
+      on_swipe_bottom:
+        logger.log: "swiped down"
+      on_swipe_up:
+        logger.log: "swiped up"
+      on_swipe_down:
+        logger.log: "swiped down"
+      on_swipe_left:
+        logger.log: "swiped left"
+      on_swipe_right:
+        logger.log: "swiped right"
       bg_image_src: cat_image
       on_load:
         - logger.log: page loaded