{"id":13825780,"url":"https://github.com/exogen/react-overflow-indicator","last_synced_at":"2026-03-17T20:33:16.630Z","repository":{"id":37952257,"uuid":"222231664","full_name":"exogen/react-overflow-indicator","owner":"exogen","description":"Detect overflow and render shadows, fades, arrows, etc.","archived":false,"fork":false,"pushed_at":"2023-01-05T01:02:10.000Z","size":10059,"stargazers_count":83,"open_issues_count":5,"forks_count":6,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-01T17:14:18.381Z","etag":null,"topics":["css","overflow","react","scrollable","scrolling"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exogen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-17T10:34:26.000Z","updated_at":"2025-03-16T15:19:57.000Z","dependencies_parsed_at":"2023-02-03T03:02:07.848Z","dependency_job_id":null,"html_url":"https://github.com/exogen/react-overflow-indicator","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Freact-overflow-indicator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Freact-overflow-indicator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Freact-overflow-indicator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exogen%2Freact-overflow-indicator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exogen","download_url":"https://codeload.github.com/exogen/react-overflow-indicator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252909032,"owners_count":21823540,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","overflow","react","scrollable","scrolling"],"created_at":"2024-08-04T09:01:26.926Z","updated_at":"2026-03-17T20:33:16.580Z","avatar_url":"https://github.com/exogen.png","language":"JavaScript","readme":"# react-overflow-indicator ⏬\n\n- Let users know when there’s more content to see in an `overflow` container, in\n  case their device hides scrollbars.\n- Uses\n  [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver)\n  for performance and accuracy – no listening for `scroll` or `resize` events.\n- Flexible: render any style of indicator you want (shadows, arrows, messages,\n  etc.) wherever you want, using any styling solution.\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth colspan=\"3\"\u003e\nSome examples: shadows, fades, icons…\n\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n![Shadow animated demo](./demo-shadow.gif)\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n![Fade animated demo](./demo-fade.gif)\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n![Icon animated demo](./demo-icon.gif)\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n## Quick Start\n\nInstall:\n\n```console\n$ yarn add react-overflow-indicator\n```\n\nImport:\n\n```js\nimport Overflow from 'react-overflow-indicator';\n```\n\nRender indicators automatically using `\u003cOverflow.Indicator\u003e` inside of\n`\u003cOverflow\u003e`:\n\n```jsx\n\u003cOverflow\u003e\n  \u003cOverflow.Content\u003e\n    Render an element or put your content directly here…\n  \u003c/Overflow.Content\u003e\n  \u003cOverflow.Indicator direction=\"down\"\u003e👇\u003c/Overflow.Indicator\u003e\n\u003c/Overflow\u003e\n```\n\n…or, use the `onStateChange` prop to react to overflow however you like:\n\n```jsx\nconst [canScroll, setCanScroll] = useState(false);\n\nreturn (\n  \u003c\u003e\n    \u003cOverflow onStateChange={state =\u003e setCanScroll(state.canScroll.down)}\u003e\n      \u003cOverflow.Content\u003e\n        Render an element or put your content directly here…\n      \u003c/Overflow.Content\u003e\n    \u003c/Overflow\u003e\n    {canScroll ? '👇' : '🌈'}\n  \u003c/\u003e\n);\n```\n\n## API\n\n\u003c!-- AUTO-GENERATED-CONTENT:START (COMPONENTS) --\u003e\n\n### Overflow\n\nThe overflow state provider. At a minimum it must contain an\n`\u003cOverflow.Content\u003e` element, otherwise it will do nothing.\n\n```jsx\n\u003cOverflow\u003e\n  \u003cOverflow.Content\u003e\n    Your element(s) here!\n  \u003c/Overflow.Content\u003e\n\u003cOverflow\u003e\n```\n\nAs with any standard element, its height must be limited in some way in order\nfor it to actually scroll. Apply that style as you would any other element, with\n`style` or `className`:\n\n```jsx\n\u003cOverflow style={{ maxHeight: 500 }}\u003e…\u003c/Overflow\u003e\n```\n\nUsage with styled-components:\n\n```jsx\nconst MyContainer = styled(Overflow)`\n  max-height: 500px;\n`;\n```\n\nAny remaining props beyond those documented below will be passed along to the\nunderlying DOM element. Use this to pass `className`, `style`, or any other\nnative attribute.\n\n#### Props\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eName\u003c/th\u003e\n\u003cth colspan=\"2\"\u003eType\u003c/th\u003e\n\u003cth\u003eDefault\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003echildren\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003eNode\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nElements to render inside the outer container. This should include an\n`\u003cOverflow.Content\u003e` element at a minimum, but should also include your scroll\nindicators if you’d like to overlay them on the scrollable viewport.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003eonStateChange\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003eFunction\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nCallback that receives the latest overflow state and an object of refs, if you’d\nlike to react to overflow in a custom way.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003etolerance\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003e\nOne\u0026nbsp;of… \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;Number \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;String\n\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e0\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nDistance (number of pixels or CSS length unit like `1em`) to the edge of the\ncontent at which to consider the viewport fully scrolled. For example, if set to\n10, then it will consider scrolling to have reached the end as long as it’s\nwithin 10 pixels of the border. You can use this when your content has padding\nand scrolling close to the edge should be good enough.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n### Overflow.Content\n\nWrapper for content to render inside the scrollable viewport. This element will\ngrow to whatever size it needs to hold its content, and will cause the parent\nviewport element to overflow. It must be rendered inside an `\u003cOverflow\u003e`\nancestor.\n\nAlthough you can style this element directly by passing additional props like\n`className` and `style`, it’s preferable to include styling on your own element\ninside `\u003cOverflow.Content\u003e` instead – otherwise you risk interfering with the\nstyles this component needs to function.\n\n#### Props\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eName\u003c/th\u003e\n\u003cth colspan=\"2\"\u003eType\u003c/th\u003e\n\u003cth\u003eDefault\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003echildren\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003eNode\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nContent to render inside the scrollable viewport.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n### Overflow.Indicator\n\nA helper component for rendering your custom indicator when the viewport is\nscrollable in a particular direction (or any direction). Must be rendered inside\nan `\u003cOverflow\u003e` ancestor.\n\nYou can provide a `direction` prop to indicate when scrolling is allowed in a\nparticular direction:\n\n```jsx\n\u003cOverflow\u003e\n  \u003cOverflow.Content\u003e…\u003c/Overflow.Content\u003e\n  \u003cOverflow.Indicator direction=\"right\"\u003e👉\u003c/Overflow.Indicator\u003e\n\u003c/Overflow\u003e\n```\n\n…or exclude it to indicate when scrolling is allowed in any direction:\n\n```jsx\n\u003cOverflow\u003e\n  \u003cOverflow.Content\u003e…\u003c/Overflow.Content\u003e\n  \u003cOverflow.Indicator\u003e←↕→\u003c/Overflow.Indicator\u003e\n\u003c/Overflow\u003e\n```\n\nThis component will mount its children when scrolling is allowed in the\nrequested direction, and unmount them otherwise. If you’d rather remain mounted\n(to allow transitions, for example), then render a function. It will be supplied\nwith a Boolean (if `direction` is supplied) or an object with `up`, `left`,\n`right`, and `down` properties:\n\n```jsx\n\u003cOverflow\u003e\n  \u003cOverflow.Indicator direction=\"down\"\u003e\n    {canScroll =\u003e (canScroll ? '🔽' : '✅')}\n  \u003c/Overflow.Indicator\u003e\n\u003c/Overflow\u003e\n```\n\n#### Props\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eName\u003c/th\u003e\n\u003cth colspan=\"2\"\u003eType\u003c/th\u003e\n\u003cth\u003eDefault\u003c/th\u003e\n\u003cth\u003eDescription\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003echildren\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003e\nOne\u0026nbsp;of… \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;Node \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;Function\n\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nIndicator to render when scrolling is allowed in the requested direction. If\ngiven a function, it will be passed the overflow state and an object containing\nthe `viewport` ref. You can use this `refs` parameter to render an indicator\nthat is also a button that scrolls the viewport (for example).\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd valign=\"top\" rowspan=\"1\"\u003edirection\u003c/td\u003e\n\u003ctd valign=\"top\" colspan=\"2\"\u003e\nOne\u0026nbsp;of… \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;'up' \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;'down' \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;'left' \u003cbr\u003e\n\u0026nbsp;\u0026nbsp;'right'\n\u003c/td\u003e\n\u003ctd valign=\"top\" align=\"right\" rowspan=\"1\"\u003e\u003c/td\u003e\n\u003ctd valign=\"top\" valign=\"top\" rowspan=\"1\"\u003e\n\nThe scrollabe direction to watch for. If not supplied, the indicator will be\nactive when scrolling is allowed in any direction.\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c!-- AUTO-GENERATED-CONTENT:END --\u003e\n\n### useOverflow\n\nThis hook provides full access to the Overflow’s context containing its current\n`state` and `refs`. While `\u003cOverflow.Indicator\u003e` should be good enough for most\nuse cases, you can use this if you have other use cases in mind. Must be used\ninside an `\u003cOverflow\u003e` ancestor.\n\nReturns an object like:\n\n```js\n{\n  state: {\n    canScroll: {\n      up: Boolean,\n      left: Boolean,\n      right: Boolean,\n      down: Boolean\n    }\n  },\n  dispatch: Function,\n  tolerance: Number | String,\n  refs: {\n    viewport: Object\n  }\n}\n```\n\n## Examples\n\n### Make the indicator a button that scrolls the viewport\n\n```jsx\n\u003cOverflow.Indicator direction=\"down\"\u003e\n  {(canScroll, refs) =\u003e (\n    \u003cbutton\n      type=\"button\"\n      onClick={() =\u003e {\n        refs.viewport.current.scrollBy({\n          top: refs.viewport.current.clientHeight,\n          behavior: 'smooth'\n        });\n      }}\n      style={{ position: 'absolute', right: 10, bottom: 10 }}\n    \u003e\n      {canScroll ? '⏬' : '✅'}\n    \u003c/button\u003e\n  )}\n\u003c/Overflow.Indicator\u003e\n```\n\n## Implementation Details\n\nInstead of the traditional method of listening for `scroll` and `resize` events,\nthis uses the more performant\n[Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API).\n\nHere, an `IntersectionObserver` watches each of the 4 sides of the viewport\nelement to see when the scrollable content extends past that edge.\n\n### What’s the purpose of each element?\n\nWhen rendered, you’ll see a structure similar to this:\n\n```\n\u003cdiv data-overflow-wrapper\u003e\n  \u003cdiv data-overflow-viewport\u003e\n    \u003cdiv data-overflow-content\u003e\n      \u003cdiv data-overflow-tolerance\u003e\u003c/div\u003e (Optional)\n      Finally, your scrollable content here…\n    \u003c/div\u003e\n  \u003c/div\u003e\n\u003c/div\u003e\n```\n\nThat seems like a lot! But each one serves a purpose – various CSS and DOM\nbehaviors make this surprisingly difficult to implement otherwise.\n\nFrom the top down:\n\n- The element with `data-scrollable-wrapper` contains everything. If you want to\n  insert some indicator overlay (like shadows, an arrow, a floating message),\n  they should usually be children of this element so that they’ll be siblings of\n  the scrollable viewport and thus will remain in their positions instead of\n  scrolling away. When you define a `height` or `max-height` to define the\n  scrollable viewport size, it will be on this element.\n- The element with `data-scrollable-viewport` is the one with `overflow: auto`.\n  It will match the size of its parent (the `data-scrollable-wrapper`). Any\n  indicators you render will usually be siblings of this element.\n- The element with `data-scrollable-content` contains your content. It will grow\n  to whatever size it needs and potentially cause the `data-scrollable-viewport`\n  element to overflow.\n- The element with `data-scrollable-tolerance` will optionally be inserted if\n  you use a nonzero `tolerance` value; in that case, this element will be\n  observed instead of the content element.\n","funding_links":[],"categories":["JavaScript","React"],"sub_categories":["React Components"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexogen%2Freact-overflow-indicator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexogen%2Freact-overflow-indicator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexogen%2Freact-overflow-indicator/lists"}