Blazorise Gestures component

Detect swipes and pointer gesture lifecycle events on wrapped content.

<Gestures> is a wrapper component for touch, pen, and mouse pointer gestures. Use it around content that should react to directional swipes, such as sidebars, galleries, item cards, or custom mobile navigation.

The high-level Swiped, Tapped, and LongPressed events are best for most scenarios. Use GestureStarted, GestureMoved, and GestureEnded when the UI needs to react while the pointer is moving.

Examples

Basic Swipe

Wrap content with Gestures and handle Swiped to react to a horizontal swipe.
Waiting for swipe

Swipe left or right inside this area.

None
<Gestures Direction="GestureDirection.Horizontal"
          TouchAction="GestureTouchAction.PanY"
          Swiped="@OnSwiped">
    <Div Border="Border.Is1.Rounded" Background="Background.Light" Padding="Padding.Is4" Style="user-select: none;">
        <Div Flex="Flex.JustifyContent.Between.AlignItems.Center" Gap="Gap.Is3">
            <Div>
                <Heading Size="HeadingSize.Is5" Margin="Margin.Is0.FromBottom">
                    @Title
                </Heading>
                <Paragraph TextColor="TextColor.Secondary" Margin="Margin.Is2.FromTop.Is0.FromBottom">
                    Swipe left or right inside this area.
                </Paragraph>
            </Div>
            <Badge Color="@BadgeColor" Pill>@DirectionText</Badge>
        </Div>
    </Div>
</Gestures>
@code {
    private string Title { get; set; } = "Waiting for swipe";

    private string DirectionText { get; set; } = "None";

    private Color BadgeColor { get; set; } = Color.Secondary;

    private Task OnSwiped( SwipeEventArgs eventArgs )
    {
        DirectionText = eventArgs.Direction.ToString();
        Title = $"Swiped {DirectionText.ToLowerInvariant()}";
        BadgeColor = eventArgs.Direction == GestureDirection.Left ? Color.Primary : Color.Info;

        return Task.CompletedTask;
    }
}

Swipe Gallery

Horizontal swipe gestures are useful for galleries, carousels, onboarding panels, and other item-by-item navigation.
1 / 3

Overview

Introduce key content one panel at a time.

Swipe left or right
<Gestures Direction="GestureDirection.Horizontal"
          TouchAction="GestureTouchAction.PanY"
          Swiped="@OnSwiped">
    <Card Background="@CurrentSlide.Background" TextColor="TextColor.White" Style="min-height: 260px; user-select: none;">
        <CardBody Padding="Padding.Is5">
            <Div Flex="Flex.Column.JustifyContent.Between" Style="min-height: 220px;">
                <Div>
                    <Badge Color="Color.Light" TextColor="TextColor.Dark" Pill>
                        @( currentIndex + 1 ) / @slides.Count
                    </Badge>
                    <Heading Size="HeadingSize.Is2" Margin="Margin.Is4.FromTop.Is2.FromBottom">
                        @CurrentSlide.Title
                    </Heading>
                    <Paragraph TextColor="TextColor.White50">
                        @CurrentSlide.Description
                    </Paragraph>
                </Div>
                <Div Flex="Flex.JustifyContent.Between.AlignItems.Center" Gap="Gap.Is3">
                    <Button Color="Color.Light" Outline Clicked="@ShowPrevious">
                        <Icon Name="IconName.ArrowLeft" />
                    </Button>
                    <Badge Color="Color.Light" TextColor="TextColor.Dark" Pill>
                        Swipe left or right
                    </Badge>
                    <Button Color="Color.Light" Outline Clicked="@ShowNext">
                        <Icon Name="IconName.ArrowRight" />
                    </Button>
                </Div>
            </Div>
        </CardBody>
    </Card>
</Gestures>
@code {
    private readonly List<GallerySlide> slides = new List<GallerySlide>
    {
        new GallerySlide( "Overview", "Introduce key content one panel at a time.", Background.Primary ),
        new GallerySlide( "Details", "Let users move through content with direct touch interaction.", Background.Info ),
        new GallerySlide( "Summary", "Keep buttons available for mouse and keyboard users.", Background.Success ),
    };

    private int currentIndex;

    private GallerySlide CurrentSlide => slides[currentIndex];

    private Task OnSwiped( SwipeEventArgs eventArgs )
    {
        if ( eventArgs.Direction == GestureDirection.Left )
            ShowNext();
        else if ( eventArgs.Direction == GestureDirection.Right )
            ShowPrevious();

        return Task.CompletedTask;
    }

    private void ShowPrevious()
    {
        currentIndex = currentIndex == 0 ? slides.Count - 1 : currentIndex - 1;
    }

    private void ShowNext()
    {
        currentIndex = currentIndex == slides.Count - 1 ? 0 : currentIndex + 1;
    }

    private class GallerySlide
    {
        public GallerySlide( string title, string description, Background background )
        {
            Title = title;
            Description = description;
            Background = background;
        }

        public string Title { get; }

        public string Description { get; }

        public Background Background { get; }
    }
}

Sidebar Toggle

Directional swipes can collapse or expand a side panel. Pair it with Animate for a slide transition while keeping regular vertical page scrolling available with TouchAction="GestureTouchAction.PanY".
Navigation

Swipe left to collapse.

Content

Swipe right to expand the panel and swipe left to collapse it.

<Gestures Direction="GestureDirection.Horizontal"
          TouchAction="GestureTouchAction.PanY"
          Swiped="@OnSwiped">
    <Div Flex="Flex.Row" Border="Border.Is1.Rounded" Background="Background.Light" Padding="Padding.Is3" Height="Height.Px( 240 )" Style="user-select: none;">
        <Div Flex="Flex.Row.Grow.Is1.AlignItems.Stretch" Gap="Gap.Is3">
            <Animate Animation="Animations.SlideRight" Duration="@SidebarAnimationDuration" Trigger="AnimationTrigger.Render" Visible="@sidebarExpanded" AnimateOnInitialRender="false" AnimatedSize="AnimatedSize.Width" Height="Height.Is100">
                <Div Height="Height.Is100" Background="Background.Primary" TextColor="TextColor.White" Padding="Padding.Is3" Border="Border.Rounded" Width="@SidebarExpandedWidth">
                    <Heading Size="HeadingSize.Is6" Margin="Margin.Is0.FromBottom">
                        Navigation
                    </Heading>
                    <Paragraph TextColor="TextColor.White50" Margin="Margin.Is2.FromTop">
                        Swipe left to collapse.
                    </Paragraph>
                </Div>
            </Animate>

            <Div Flex="Flex.Grow.Is1" Padding="Padding.Is2">
                <Heading Size="HeadingSize.Is5" Margin="Margin.Is0.FromBottom">
                    Content
                </Heading>
                <Paragraph Margin="Margin.Is2.FromTop">
                    Swipe right to expand the panel and swipe left to collapse it.
                </Paragraph>
                <Button Color="Color.Primary" Outline Clicked="@ToggleSidebar">
                    <Icon Name="@( sidebarExpanded ? IconName.Compress : IconName.Expand)" Margin="Margin.Is2.FromEnd" />
                    @( sidebarExpanded ? "Collapse" : "Expand" )
                </Button>
            </Div>
        </Div>
    </Div>
</Gestures>
@code {
    private static readonly TimeSpan SidebarAnimationDuration = TimeSpan.FromMilliseconds( 250 );

    private static readonly IFluentSizing SidebarExpandedWidth = Width.Px( 160 );

    private bool sidebarExpanded = true;

    private Task OnSwiped( SwipeEventArgs eventArgs )
    {
        if ( eventArgs.Direction == GestureDirection.Left )
        {
            sidebarExpanded = false;
        }
        else if ( eventArgs.Direction == GestureDirection.Right )
        {
            sidebarExpanded = true;
        }

        return Task.CompletedTask;
    }

    private void ToggleSidebar()
    {
        sidebarExpanded = !sidebarExpanded;
    }
}

Gesture Lifecycle

Use lifecycle events when the UI should update while the pointer is moving, for example a progress indicator, custom drawer motion, or swipe-to-dismiss preview.
Swipe progress

Move horizontally inside this card to update the progress while the gesture is active.

Waiting None Delta X: 0
<Gestures Direction="GestureDirection.Horizontal"
          TouchAction="GestureTouchAction.PanY"
          GestureStarted="@OnGestureStarted"
          GestureMoved="@OnGestureMoved"
          GestureEnded="@OnGestureEnded"
          Swiped="@OnSwiped">
    <Card Border="Border.Is1.Rounded" Background="Background.Light" Style="user-select: none;">
        <CardBody Padding="Padding.Is4">
            <Heading Size="HeadingSize.Is5" Margin="Margin.Is0.FromBottom">
                Swipe progress
            </Heading>
            <Paragraph TextColor="TextColor.Secondary" Margin="Margin.Is2.FromTop">
                Move horizontally inside this card to update the progress while the gesture is active.
            </Paragraph>
            <Progress Value="@progress" Color="@progressColor" />
            <Div Flex="Flex.Wrap" Gap="Gap.Is2" Margin="Margin.Is3.FromTop">
                <Badge Color="Color.Primary" Pill>@state</Badge>
                <Badge Color="Color.Info" Pill>@direction</Badge>
                <Badge Color="Color.Secondary" Pill>Delta X: @deltaX</Badge>
            </Div>
        </CardBody>
    </Card>
</Gestures>
@code {
    private int progress;

    private string state = "Waiting";

    private string direction = "None";

    private string deltaX = "0";

    private Color progressColor = Color.Primary;

    private Task OnGestureStarted( GestureEventArgs eventArgs )
    {
        state = "Started";
        progress = 0;
        progressColor = Color.Primary;
        UpdateDetails( eventArgs );

        return Task.CompletedTask;
    }

    private Task OnGestureMoved( GestureEventArgs eventArgs )
    {
        state = "Moving";
        progress = System.Math.Min( 100, System.Convert.ToInt32( System.Math.Abs( eventArgs.DeltaX ) ) );
        UpdateDetails( eventArgs );

        return Task.CompletedTask;
    }

    private Task OnGestureEnded( GestureEventArgs eventArgs )
    {
        state = eventArgs.Canceled ? "Canceled" : "Ended";
        UpdateDetails( eventArgs );

        return Task.CompletedTask;
    }

    private Task OnSwiped( SwipeEventArgs eventArgs )
    {
        progress = 100;
        progressColor = Color.Success;
        UpdateDetails( eventArgs );

        return Task.CompletedTask;
    }

    private void UpdateDetails( GestureEventArgs eventArgs )
    {
        direction = eventArgs.Direction.ToString();
        deltaX = eventArgs.DeltaX.ToString( "0" );
    }
}

Touch action

TouchAction maps to the browser CSS touch-action behavior. For horizontal swipe areas inside a scrollable page, use GestureTouchAction.PanY so the page can still scroll vertically. For vertical swipe areas, use GestureTouchAction.PanX. Use GestureTouchAction.None only for surfaces where the component should own all touch movement.

Native Drag

Gestures prevents native browser dragging by default so images and links inside the gesture area do not show a drag preview while the user is swiping. Set PreventNativeDrag="false" if the content should keep native drag behavior.

Gestures does not disable text selection. If a gesture surface contains text and is meant to be dragged or swiped as a single control, you may want to apply CSS such as user-select: none; to that surface. This prevents accidental text selection during mouse dragging on desktop. Avoid applying it to areas where users should be able to select or copy text.

Best Practices

Optional Gestures

Every important swipe action should also be available through a visible button or another non-gesture control. This keeps the interaction usable with keyboards, assistive technology, and devices where the gesture is difficult to perform.

Avoid Accidental Activation

Use direction and distance thresholds that ignore small or unintended movements, and avoid intercepting ordinary page scrolling unless the gesture area clearly requires it. Show feedback while the gesture is in progress, but commit state only after it completes successfully.

API

See the API reference for the parameters, events, methods, and related types available to the components covered on this page.

On this page