{"id":15131787,"url":"https://github.com/traviskn/react-router-native-stack","last_synced_at":"2025-09-28T23:32:18.977Z","repository":{"id":24695020,"uuid":"101512169","full_name":"Traviskn/react-router-native-stack","owner":"Traviskn","description":"A stack navigation component for react-router-native","archived":true,"fork":false,"pushed_at":"2022-12-08T17:15:50.000Z","size":5054,"stargazers_count":170,"open_issues_count":49,"forks_count":36,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-01-11T08:49:00.153Z","etag":null,"topics":["animation","navigation","react-native","react-router","router","stack","transitions"],"latest_commit_sha":null,"homepage":null,"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/Traviskn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-08-26T20:43:11.000Z","updated_at":"2024-10-22T11:13:24.000Z","dependencies_parsed_at":"2022-07-25T11:17:50.224Z","dependency_job_id":null,"html_url":"https://github.com/Traviskn/react-router-native-stack","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Traviskn%2Freact-router-native-stack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Traviskn%2Freact-router-native-stack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Traviskn%2Freact-router-native-stack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Traviskn%2Freact-router-native-stack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Traviskn","download_url":"https://codeload.github.com/Traviskn/react-router-native-stack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234575214,"owners_count":18854924,"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":["animation","navigation","react-native","react-router","router","stack","transitions"],"created_at":"2024-09-26T04:01:06.361Z","updated_at":"2025-09-28T23:32:12.848Z","avatar_url":"https://github.com/Traviskn.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-router-native-stack\n\n[![npm version](https://img.shields.io/npm/v/react-router-native-stack.svg?style=flat-square)](https://www.npmjs.com/package/react-router-native-stack)\n[![downloads](https://img.shields.io/npm/dm/react-router-native-stack.svg?style=flat-square)](https://www.npmjs.com/package/react-router-native-stack)\n\nA Stack component for React Router v4 on React Native.\n\n## Disclaimer\n\nThis library is in an alpha state.  I am still experimenting with the\ntransitions and animations, and the API will likely evolve and change.  I'd\nlove for you to try it out and give feedback so that we can get to a production\nready state!\n\n## Motivation\n\nReact Router v4 supports react native, but doesn't include any animated transitions\nout of the box.  I created this component to support card stack style screen transitions.\n\nHere's a basic demo:\n\n![SimpleStack Example Screen Capture](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/simple-stack-ios.gif)\n\nYou can run the above demo on Expo with this link: https://exp.host/@traviskn/simplestack\n\n## Installation\n\nInstall `react-router-native` and this package:\n\n`npm install react-router-native react-router-native-stack --save`\n\n## Usage\n\nHere's a simple working example of using the stack.\n\n```javascript\nimport React, { Component } from 'react';\nimport { Button, StyleSheet, Text, View } from 'react-native';\nimport { NativeRouter, Route } from 'react-router-native';\nimport Stack from 'react-router-native-stack';\n\nfunction Home({ history }) {\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cText\u003eHome Page\u003c/Text\u003e\n\n      \u003cButton title=\"Pizza Page\" onPress={() =\u003e history.push('/page/pizza')} /\u003e\n\n      \u003cButton title=\"Taco Page\" onPress={() =\u003e history.push('/page/taco')} /\u003e\n\n      \u003cButton title=\"Hamburger Page\" onPress={() =\u003e history.push('/page/hamburger')} /\u003e\n    \u003c/View\u003e\n  );\n}\n\nfunction Page({ history, match }) {\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cText\u003eYou are on a {match.params.name} Page!\u003c/Text\u003e\n\n      \u003cButton title=\"Go Back\" color=\"red\" onPress={() =\u003e history.goBack()} /\u003e\n\n      \u003cButton title=\"Pizza Page\" onPress={() =\u003e history.push('/page/pizza')} /\u003e\n\n      \u003cButton title=\"Taco Page\" onPress={() =\u003e history.push('/page/taco')} /\u003e\n\n      \u003cButton title=\"Hamburger Page\" onPress={() =\u003e history.push('/page/hamburger')} /\u003e\n    \u003c/View\u003e\n  );\n}\n\nexport default class App extends Component {\n  render() {\n    return (\n      \u003cNativeRouter\u003e\n        \u003cStack\u003e\n          \u003cRoute exact path=\"/\" component={Home} /\u003e\n          \u003cRoute path=\"/page/:name\" component={Page} /\u003e\n        \u003c/Stack\u003e\n      \u003c/NativeRouter\u003e\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  screen: {\n    flex: 1,\n    flexDirection: 'column',\n    alignItems: 'center',\n    justifyContent: 'space-around',\n  },\n});\n```\n\nThis is what the above code looks like running on iOS:\n\n![Usage Example Screen Capture](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/usage-ios.gif)\n\nThe stack component uses a [Switch](https://reacttraining.com/react-router/native/api/Switch)\ninternally to match the current route. It listens for `'PUSH'` and `'POP'` actions\non history to determine whether to transition forward or backwards. It manages a\nPanResponder to allow swiping back through the route stack. It keeps track of the\nhistory index when it mounts so that it knows to stop allowing the swipe back transition\nwhen you reach the beginning index.\n\n## Animation Options\n\nIn the examples so far you have seen the default iOS transition animation,\n`'slide-horizontal'`. In addition to that the stack also supports `'slide-vertical'`,\n`'fade-vertical'`, and `'cube'`.\n\nif you add an `animationType=\"slide-vertical\"` prop to the stack in the previous\nexample, this is the result:\n\n![Slide Vertical Usage Example Screen Capture](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/slide-vertical-ios.gif)\n\n`'fade-vertical'` is the default for Android, and looks like this:\n\n![Fade Vertical Usage Example Screen Capture](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/fade-vertical-android.gif)\n\nAnd finally, here's a demo of `animationType=\"cube\"`:\n\n![Cube Example Screen Capture](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/cube-ios.gif)\n\nThere is also an animation type of `'none'` if you need to disable animations.\n\n### Animating `history.replace()`\n\nSometimes it is desirable to animate a route replace, i.e to animate back to a specific route (without using `history.go(-n)`).\n\nUse `replaceTransitionType` as a prop, with either `POP` or `PUSH` to animate the `REPLACE` event.\n\n```javascript\n  \u003cStack replaceTransitionType=\"POP\" /\u003e // A call to `history.replace(routePath)` will now transition using the `POP` animation type.\n```\n\n## Gesture Handling Options\n\nBy default the stack component allows swiping back for the `slide-horizontal` and `cube` animation types.  If you want to\ndisable this, you can pass in a `gestureEnabled` prop set to false.\n\n```javascript\n// This stack will not respond to the swipe back gesture\n\u003cStack gestureEnabled={false}\u003e\n  {/* Your routes here */}\n\u003c/Stack\u003e\n```\n\n## Customizing Animation Type Per-Route\n\nIf you need, you can configure the animation type on a per-route basis by adding\nan `animationType` prop to a specific route.  As an example, consider that we took\nour previous example and had a separate route for each page:\n\n```javascript\nimport React, { Component } from 'react';\nimport { Button, StyleSheet, Text, View } from 'react-native';\nimport { NativeRouter, Route } from 'react-router-native';\nimport Stack from 'react-router-native-stack';\n\nfunction Home({ history }) {\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cText\u003eHome Page\u003c/Text\u003e\n\n      \u003cButton title=\"Pizza Page\" onPress={() =\u003e history.push('/page/pizza')} /\u003e\n\n      \u003cButton title=\"Taco Page\" onPress={() =\u003e history.push('/page/taco')} /\u003e\n\n      \u003cButton title=\"Hamburger Page\" onPress={() =\u003e history.push('/page/hamburger')} /\u003e\n    \u003c/View\u003e\n  );\n}\n\nfunction Page({ history, match }) {\n  return (\n    \u003cView style={styles.screen}\u003e\n      \u003cText\u003eYou are on a {match.url.replace('/page/', '')} Page!\u003c/Text\u003e\n\n      \u003cButton title=\"Go Back\" color=\"red\" onPress={() =\u003e history.goBack()} /\u003e\n\n      \u003cButton title=\"Pizza Page\" onPress={() =\u003e history.push('/page/pizza')} /\u003e\n\n      \u003cButton title=\"Taco Page\" onPress={() =\u003e history.push('/page/taco')} /\u003e\n\n      \u003cButton title=\"Hamburger Page\" onPress={() =\u003e history.push('/page/hamburger')} /\u003e\n    \u003c/View\u003e\n  );\n}\n\nexport default class App extends Component {\n  render() {\n    return (\n      \u003cNativeRouter\u003e\n        \u003cStack\u003e\n          \u003cRoute exact path=\"/\" component={Home} /\u003e\n          \u003cRoute path=\"/page/pizza\" component={Page} /\u003e\n          \u003cRoute path=\"/page/taco\" animationType=\"slide-vertical\" component={Page} /\u003e\n          \u003cRoute path=\"/page/hamburger\" component={Page} /\u003e\n        \u003c/Stack\u003e\n      \u003c/NativeRouter\u003e\n    );\n  }\n}\n\nconst styles = StyleSheet.create({\n  screen: {\n    flex: 1,\n    flexDirection: 'column',\n    alignItems: 'center',\n    justifyContent: 'space-around',\n  },\n});\n```\n\nWith this updated code we have configured the taco page to use a `slide-vertical`\nanimation, but all the other pages will use the default `slide-horizontal` animation.\nThe taco page will control the animation type when it pushes onto the stack, and\nwhen it pops off of the stack. Here's how it looks:\n\n![Per-Route Animation Type Example](https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/per-route-animation-type-ios.gif)\n\n## Specifying a Header and/or Footer component per route\n\n**Scenario 1:** Sometimes you'll want a fixed header/footer that *doesn't* animate between routes.\n**Scenario 2:** At other times you'll want to transition between routes that have *different* headers/footers.\n**Scenario 3:** And finally, you may want to transition to a route that has *no* fixed header or footer.\n\nAll this is possible by specifying a `headerComponent` and/or `footerComponent` prop on your routes:\n\n```js\nexport default class App extends Component {\n  render() {\n    return (\n      \u003cNativeRouter\u003e\n        \u003cStack\u003e\n          \u003cRoute exact path=\"/\" component={Home} headerComponent={Header} /\u003e\n          \u003cRoute exact path=\"/fullscreen\" component={FullScreen} /\u003e\n          \u003cRoute exact path=\"/page/pizza\" component={Page} headerComponent={PizzaHeader} /\u003e\n          \u003cRoute path=\"/page/:name\" component={Page} headerComponent={Header} /\u003e\n        \u003c/Stack\u003e\n      \u003c/NativeRouter\u003e\n    );\n  }\n}\n```\n\nGo to `examples/App` to see the full setup for the above.\n\n**Scenario 1:** If your Header and/or Footer matches that of the route you're transitioning to, then that component\nwill be considered \"fixed\" and will not be included in the transition animation.\n\n\u003cimg src=\"https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/fixed-header.gif\" width=\"350\" /\u003e\n\n**Scenario 2:** If the route you're transitioning to contains a different Header/Footer than the previous route,\nthen the Header/Footer for both routes will be contained *within* the transition area, and will be\nincluded in the transition animation.\n\n\u003cimg src=\"https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/different-header.gif\" width=\"350\" /\u003e\n\n**Scenario 3:** If you're transitioning to/from a route that contains *no* Header/Footer, then that's treated the\nsame as Scenario 2, and any Headers/Footers will be contained within the transition area. The route that\ncontains no Header/Footer will obviously have none rendered, and therefore the main `component` will\noccupy the full area available.\n\n\u003cimg src=\"https://raw.githubusercontent.com/traviskn/react-router-native-stack/master/media/no-header.gif\" width=\"350\" /\u003e\n\n**IMPORTANT NOTE:** The above approach makes no assumption as to the look/feel of the Header/Footer\ncomponent, including internal animation. As the React Native world moves towards iOS and Android\napps being built simultaneously, using custom header/footer/navigation components that make sense\nwithin the design system of the given app, it follows that we should give the developer full power\nover how to animate the mounting/unmounting of the Header and Footer components themselves (as well\nas the elements within them). We do pass the Stack component's internal animated value into the Header\nand/or Footer component you provide as an `animatedValue` prop that you can use to build your own\nanimations that run in sync with the Stack's animations.\n\nUsing the `headerComponent` and `footerComponent` props is a simple way to either include or exclude\ncomponents from the route transition animation.\n\n## Nested routes\n\nWhere one of the Routes in the Stack have nested Routes the default behaviour is to\nanimate between pages as if you were changing to completely different route.\n\nSometimes this behaviour is not what you want (for example when creating a page\nto show items, where items can be deep linked to, but only form part of the page).\nIn this case you can add a key to the Route, and \"self\"-transitions are then\nignored.\n\n```javascript\n  \u003cStack\u003e\n    \u003cRoute exact path=\"/\" component={Home} /\u003e\n    { /* animates moving to /items, but not when changing itemId */ }\n    \u003cRoute path=\"/items/:itemId?\" component={Items} key=\"items\"/\u003e\n  \u003c/Stack\u003e\n\n  const Items = ({match}) =\u003e\n    \u003cView\u003e\n       \u003cText\u003eItems finder\u003c/Text\u003e\n       \u003cText\u003eLooking at item {match.params.itemId}\u003c/Text\u003e\n    \u003c/View\u003e\n```\n\n## `isAnimating` handler\n\nSometimes you'll want your app to respond a certain way while a route transition animation\nis occurring. For example you may want to prevent custom `\u003cLink\u003e`'s  and `\u003cButton\u003e`'s from\nperforming their usual behaviour (manipulating the `history`) should a route transition be\noccurring at that very moment. This helps prevent bugs around users pressing the \"back\" button\nbefore the current route has finished animating in. To this end, you can pass the `Stack` an\n`isAnimating` function which will be called with a boolean value based on the current\nstate of the route transition.\n\n```javascript\n\u003cStack\n  animationType='slide-horizontal'\n  isAnimating={(value) =\u003e {\n    reduxStore.dispatch(myActionHere(value));\n  }}\n\u003e\n  \u003cRoute exact= path='/' component={Home} /\u003e\n  \u003cRoute path='/page/:name' component={Page} /\u003e\n\u003c/Stack\u003e\n```\n\nOne way of handling the above scenario would be to use a redux dispatcher as my `isAnimating`\nhandler and subsequently have all my `\u003cButton\u003e`'s and `\u003cLink\u003e`'s subscribe to the necessary bit\nof global state to determine whether to `disable` themselves or not. But ultimately, what your\n`isAnimating` function looks like, and how you leverage it, is up to you!\n\n## Known Limitations\n\nCurrently the stack has no built-in animations for headers or footers, but that\nfeature is a work in progress! We do pass down the Stack component's internal\nanimated value as a prop to the header and/or footer components you add as props\nto your routes, so you can potentialy build your own custom animation. We hope to\nget more options working soon.\n\nMany stack navigators keep all screens in the stack mounted when you push new\nscreens onto the stack.  This library is different, in that it unmounts the previous\nroute's screen when a new one is pushed on.  If you have state that needs to be\nmaintained even after a screen unmounts, you will need to store that state in a\nparent component that contains the stack or possibly use another state management\nsolution such as AsyncStorage, Redux, or MobX.\n\nA common use case for a cube transition is to swipe forward to the next route,\nbut currently it only supports swiping back to the previous route.  An API to\nenable swiping forward to a new route is something I hope to work on soon.\n\nThe cube animation doesn't work quite as well on Android as it does on iOS.  I\nhope to be able to adjust the animation configuration a bit to make it look more\nconsistent.\n\nI have made several assumptions about the history route stack while using this library.\nI assume in particular that history never mutates, and that you always navigate\nforward by pushing and backward by popping routes.  It could be that in cases where\nyou need to deep link or redirect to a specific location in the app that you haven't\nbuilt up the expected route stack, and this component won't allow swiping back\nwhen you need it to.\n\nAs I research more use cases I hope to be able to create a more flexible API to\nsupport them.\n\n## Acknowledgements\n\nI drew a lot of inspiration from other libraries and code samples.\n\nObviously this library wouldn't exist without the fantastic [React Router](https://reacttraining.com/react-router)!\n\n[React Navigation](https://reactnavigation.org/) has become one of the leading\nnavigation solutions for react native, I have used it in many personal and work\nprojects and I referenced it as a guide for implementing many of the transition\nanimations in this library.\n\nFor the cube animation I used [this example](https://github.com/underscopeio/cube-transition-example)\nby @underscopeio as a reference.\n\nMany thanks to the authors and maintainers of these open source libraries!\n\n## More\n\nYou may be asking, \"What about tab and drawer navigation?\".  As it turns out,\nthere are already many great open source components to enable drawer and tab\nnavigation, and you can already use React Router to drive the state of those\ncomponents.  I hope to add more examples to this repository soon demonstrating\nthis. For now know that the Stack is just one of several components that you can\ncombine with React Router to enable just about any navigation pattern you need!\n\nCheck out the `examples/` folder in this repository for more usage examples.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftraviskn%2Freact-router-native-stack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftraviskn%2Freact-router-native-stack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftraviskn%2Freact-router-native-stack/lists"}