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.

Text selection

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.

API

Parameters

Parameter Description TypeDefault
ChildContent

Gets or sets the content that will be observed for gestures.

RenderFragmentnull
Direction

Gets or sets the directions that can produce swipe events.

Possible values:None, Left, Right, Horizontal, Up, Down, Vertical, All

GestureDirectiondefault(GestureDirection)
Disabled

Disables gesture handling.

boolfalse
LongPressDuration

Gets or sets the duration in milliseconds required to recognize a long press.

int500
LongPressMoveTolerance

Gets or sets the movement tolerance in pixels before a pending long press is canceled.

double10
MoveThrottleInterval

Gets or sets the minimum interval, in milliseconds, between GestureMoved callbacks.

int50
SwipeThreshold

Gets or sets the minimum swipe distance in pixels.

double50
SwipeVelocityThreshold

Gets or sets the minimum swipe velocity in pixels per millisecond.

double0.3
TapMaximumDistance

Gets or sets the maximum movement in pixels that can still be recognized as a tap.

double10
TapMaximumDuration

Gets or sets the maximum duration in milliseconds that can still be recognized as a tap.

int300
TouchAction

Gets or sets the browser touch-action behavior applied to the gestures element.

Possible values:Auto, None, PanX, PanY, Manipulation

GestureTouchActionGestureTouchAction.Auto

Events

Event Description Type
GestureEnded

Raised when a gesture ends or is canceled.

EventCallback<GestureEventArgs>
GestureMoved

Raised while an active gesture moves. The callback is throttled by MoveThrottleInterval.

EventCallback<GestureEventArgs>
GestureStarted

Raised when a gesture starts.

EventCallback<GestureEventArgs>
LongPressed

Raised when an active gesture is held long enough to be recognized as a long press.

EventCallback<LongPressEventArgs>
Swiped

Raised when a completed gesture is recognized as a swipe.

EventCallback<SwipeEventArgs>
Tapped

Raised when a completed gesture is recognized as a tap.

EventCallback<TapEventArgs>

Methods

Method DescriptionReturnParameters
OnGestureEnded Handles a gesture end notification from the browser. TaskGestureEventArgs eventArgs
OnGestureMoved Handles a gesture move notification from the browser. TaskGestureEventArgs eventArgs
OnGestureStarted Handles a gesture start notification from the browser. TaskGestureEventArgs eventArgs
OnLongPressed Handles a long-press notification from the browser. TaskLongPressEventArgs eventArgs
OnSwiped Handles a swipe notification from the browser. TaskSwipeEventArgs eventArgs
OnTapped Handles a tap notification from the browser. TaskTapEventArgs eventArgs
On this page