<scroll-view>

A container that scrolls, like a <div> with overflow: auto. It renders all its children at once, so use <list> for long lists.

Usage

src/app/feed.ts
import { Component, signal } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';

@Component({
  selector: 'app-feed',
  imports: [LYNX_ELEMENTS],
  template: `
    <scroll-view
      scroll-orientation="vertical"
      style="height: 100vh;"
      (bindscrolltolower)="loadMore()"
    >
      @for (post of posts(); track post.id) {
        <text>{{ post.title }}</text>
      }
    </scroll-view>
  `,
})
export class Feed {
  readonly posts = signal([{ id: 1, title: 'First post' }]);
  loadMore(): void {}
}

Import LYNX_ELEMENTS in your component (see Elements).

Warning

A <scroll-view> without a fixed height grows to fit its content and never scrolls. flex: 1 on it isn't enough: put flex: 1 on a parent column and give the scroll view height: 100%.

Attributes

AttributeTypeDefaultDescription
scroll-orientation'vertical' | 'horizontal''vertical'Scroll direction. It replaces the old scroll-x and scroll-y attributes.
enable-scrollbooleantrueWhether the user can scroll it by dragging.
bouncesbooleantrueiOS only. Rubber-band bounce at the edges. This is Lynx's default: AngularLynx doesn't change it.
scroll-bar-enablebooleantrueShows the scroll bar.
upper-thresholdnumber0Distance in px from the top (or left) edge at which bindscrolltoupper fires.
lower-thresholdnumber0Distance in px from the bottom (or right) edge at which bindscrolltolower fires.
initial-scroll-offsetnumber0Starting scroll position in px. It applies once.
initial-scroll-to-indexnumber0Starts scrolled to the child at this index. It applies once, and every direct child needs flatten="false".
experimental-android-enable-new-overflowbooleantrueAndroid only, Lynx 4.2 and later. Experimental clipping implementation.
enable-nested-scrollbooleanfalseiOS and Android. Lets it scroll inside another scroll container: the inner one scrolls first.

Plus the common attributes, such as id, class and style.

Events

Read the payload from $event.detail (see Handling taps and events).

EventdetailDescription
bindscroll{ scrollTop: number; scrollLeft: number; scrollHeight: number; scrollWidth: number; deltaX: number; deltaY: number }Fires while it scrolls. Positions are in px; deltaX/deltaY are the change since the last event.
bindscrollendSame as bindscrollScrolling, including momentum, has stopped.
bindscrolltoupperSame as bindscrollScrolled to within upper-threshold of the top (or left) edge.
bindscrolltolowerSame as bindscrollScrolled to within lower-threshold of the bottom (or right) edge. Use it to load more.
bindscrolltoupperedgeSame as bindscrolliOS and Android. Reached the very top (or left) edge.
bindscrolltoloweredgeSame as bindscrolliOS and Android. Reached the very bottom (or right) edge.
bindscrolltonormalstateSame as bindscrolliOS and Android. Moved away from an edge.
bindcontentsizechangedSame as bindscrolliOS (Lynx 4.1 and later) and Android. The content's size changed.

Plus the common events, such as bindtap.

Methods

These UI methods (Glossary) scroll it from code. Call them with invoke() on the element (see Call element methods). A call from a handler returns nothing: to read a result, call invoke() in a main-thread script instead. To bring one child into view, call scrollIntoView on that child.

MethodParametersDescription
scrollTo{ offset?: number; index?: number; smooth?: boolean }Scrolls to the child at index (default 0), plus offset px. smooth animates it.
scrollBy{ offset: number }Scrolls by offset px.
autoScroll{ rate: number; start: boolean }start: true scrolls continuously at rate px per second (negative scrolls back); start: false stops.
getScrollInfo—iOS and Android. Returns { scrollX, scrollY, scrollRange } in px.
takeContentScreenshot{ format?: 'jpeg' | 'png'; scale?: number }iOS (Lynx 4.1 and later) and Android. Captures all the content, not only the visible part, as { data, width, height } with base64 data.
import { Component, ElementRef, viewChild } from '@angular/core';
import { LYNX_ELEMENTS } from '@blotch/angular-lynx';

@Component({
  selector: 'app-feed',
  imports: [LYNX_ELEMENTS],
  template: `
    <scroll-view #feed scroll-orientation="vertical" style="height: 400px;">
      <text>Long content</text>
    </scroll-view>
    <view (bindtap)="toTop()"><text>Back to top</text></view>
  `,
})
export class Feed {
  readonly feed = viewChild.required('feed', { read: ElementRef });

  toTop(): void {
    this.feed().nativeElement.invoke?.('scrollTo', { offset: 0, smooth: true });
  }
}
  • <list>: virtualized scrolling for long lists
  • <scroll-coordinator>: a list under a collapsing header. Nested scroll views don't hand scrolling to each other.
  • CSS on Lynx: heights, position: sticky and z-index inside a scroll view
  • Scroll Area: the UI component built on it