#
import { APIPipeLiningDiagram } from "../../../components/diagrams/APIPipeLiningDiagram";
# API Pipelining
As shown above, you can reduce the time taken for initial rendering by simultaneously initializing the React app and making API requests in the entry file. By utilizing the Stackflow Future API, you can implement API pipelining in a clean manner.
```tsx showLineNumbers filename="entry.ts" copy
import { makeTemplate } from "@stackflow/plugin-history-sync";
import { config } from "./stackflow/stackflow.config";
async function main() {
let initialLoaderData: unknown | null = null;
for (const activity of config.activities) {
const t = makeTemplate({ path: activity.route });
const match = t.parse(location.pathname + location.search);
if (!match) {
continue;
}
// 1. Request API data (do not await)
initialLoaderData = activity.loader({ params: match as any });
break;
}
// 2. Download the React app simultaneously.
const { renderApp } = await import("./renderApp");
// 3. Combine them.
renderApp({ initialLoaderData });
}
main();
```
By passing initialLoaderData to Stack, it overrides the result with the received loaderData instead of executing the first loader.
```tsx showLineNumbers filename="renderApp.ts" copy
export function renderApp({ initialLoaderData }: { initialLoaderData: unknown }) {
const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(
// Error and loading handling is possible in React
);
}
```
#
import { Callout } from "nextra-theme-docs";
# API Changes
## Stackflow React Future
- The new API can be accessed via `@stackflow/react/future`.
Use it like this:
```tsx showLineNumbers filename="Stack.tsx" copy
import { stackflow } from "@stackflow/react/future";
import HomeActivity from "../components/HomeActivity";
import MyProfileActivity from "../components/MyProfileActivity";
// Pass the created config as an argument
import { config } from "./stackflow.config";
export const { Stack } = stackflow({
config,
// Inject React components with the names declared in Config
components: {
HomeActivity,
MyProfileActivity,
},
// You no longer need to declare routes separately for the History Sync Plugin
// By adding the plugin as below, a red line will appear in stackflow.config.ts
plugins: [
historySyncPlugin({
config,
fallbackActivity: () => "FeedActivity",
}),
],
});
```
```tsx showLineNumbers filename="stackflow.config.ts" copy
import { defineConfig } from "@stackflow/config";
export const config = defineConfig({
activities: [
{
name: "HomeActivity",
// You can declare the settings required by the History Sync Plugin in stackflow.config
route: "/",
},
{
name: "MyProfileActivity",
route: "/my-profile",
},
{
name: "ArticleActivity",
route: {
path: "/articles/:articleId",
decode(params) {
return {
articleId: Number(params.get("articleId")),
};
},
},
},
],
transitionDuration: 270,
});
```
**"Why don't we declare React components together in the Config?"**
The core design of Stackflow Config is to be a container for static information without framework dependencies. Since Stackflow Config should not have React dependencies, it is designed to separate this part and add React dependencies (components) in the `@stackflow/react` configuration section.
## Type Safety
Previously, types were inferred through the Props type of React components. Now, it is changed to be inferred from the Config.
To add activity parameter types to the Config, declare them as follows.
```typescript
declare module "@stackflow/config" {
interface Register {
HomeActivity: {
regionId: string;
referrer?: string;
}
}
}
```
The type of the activity is registered globally, and you can use utility types as shown below.
```tsx showLineNumbers filename="HomeActivity.tsx" copy
import type { ActivityComponentType } from "@stackflow/react/future";
const HomeActivity: ActivityComponentType<"HomeActivity"> = ({ params }) => {
params.regionId // string
return (
...
)
}
```
You can also gather these declarations in one place or separate them by activity file (Co-location), so you can manage them as you wish.
```typescript showLineNumbers filename="HomeActivity.tsx" copy
declare module "@stackflow/config" {
interface Register {
HomeActivity: {
regionId: string;
referrer?: string;
}
}
}
```
```typescript showLineNumbers filename="MyProfileActivity.tsx" copy
declare module "@stackflow/config" {
interface Register {
MyProfileActivity: {
userId: string;
}
}
}
```
## `useFlow()`, `useStepFlow()`
You no longer need to create hooks like `useFlow()` and `useStepFlow()` using functions like `flow()`. You can import them directly.
```tsx showLineNumbers filename="HomeActivity.tsx" copy
import { useFlow, useStepFlow } from "@stackflow/react/future";
const { push } = useFlow();
const { pushStep } = useStepFlow<"HomeActivity">()
push("...", { ... }) // Typed
pushStep({ ... }) // Typed
```
Type safety is naturally ensured
### Destructive Changes
The function names of `useStepFlow()` have been changed.
- Previous: `stepPush()`, `stepReplace()`, `stepPop()`
- **Changed: `pushStep()`, `replaceStep()`, `popStep()`**
- The specification has been modified so that the function names start with a verb.
## ``
Similarly, there is no need to create the `` component using functions like `createLinkComponent()`. You can import it directly. Additionally, the existing Preload behavior has been removed.
```tsx showLineNumbers filename="MyComponent.tsx" copy
import { Link } from "@stackflow/link/future";
function MyComponent() {
return (
{/* ... */}
)
}
```
The behavior of preloading the API when the `` component appears in the viewport has significant performance benefits but greatly increases server load, so it has been found to be rarely used. Therefore, the existing preloading behavior, which was the default, has been removed. In the future, we plan to add props to the `` component to allow developers to finely control the preloading policy.
#
# Code Splitting
To properly render transition effects after splitting code by activity, you need to set it up as follows.
```tsx
// as-is:
import { lazy } from "react";
import { stackflow } from "@stackflow/react/future";
stackflow({
// ...
components: {
MyActivity: lazy(() => import("./activities/MyActivity")),
},
});
// to-be:
import { stackflow, lazy } from "@stackflow/react/future";
stackflow({
// ...
components: {
// replace `lazy()` from `@stackflow/react/future`
MyActivity: lazy(() => import("./activities/MyActivity")),
},
});
```
This is to pause the stack state mutating while fetching the corresponding JS asset (while the Promise is pending), and resume the state mutating again once the loading is complete.
#
import { Callout } from "nextra-theme-docs";
import { APITable } from "../../../components/APITable";
# `@stackflow/config`
### Config
- Introduce the concept of `Config` in Stackflow.
- Allows static declaration of activities without React dependency.
You can write as follows.
```tsx showLineNumbers filename="stackflow.config.ts" copy
import { defineConfig } from "@stackflow/config";
export const config = defineConfig({
activities: [
{
name: "HomeActivity",
},
{
name: "MyProfileActivity",
}
],
transitionDuration: 270,
});
```
Now, when additional configuration is needed in a plugin, you can extend `@stackflow/config` to receive the necessary information for the plugin's operation, not just through the plugin function's parameters.
### `decorate` API
You can extend the `Config` type as follows to utilize `config` in various places.
```typescript
declare module "@stackflow/config" {
interface Config> {
relayEnvironment: RelayEnvironment;
}
}
config.decorate("relayEnvironment", myRelayEnvironment);
```
### API
| | | |
| ------------------ | ----------------- | -------------------------------------------------- |
| activities | `ActivityDefinition[]` | An array of activities. |
| transitionDuration | `number` | Duration of the transition animation. |
| initialActivity | `() => ActivityDefinition["name"]` | The initial activity. |
#
import { Callout } from "nextra-theme-docs";
# Stackflow Future API
- We are revamping Stackflow's interface to optimize initial loading performance.
- Adding support for a Loader API without React dependency.
## Future API?
- The Future API is a preview version of Stackflow 2.0 API.
- Since it is not a finalized API, your feedback on any inconveniences you encounter while using it is highly appreciated and can contribute to further improvements.
- The existing API can still be used as the internal workings remain unchanged. However, the existing API will be removed after the 2.0 version update.
### Minimum Package Version
To use the newly added Future API, please update the following packages to the latest version.
- `@stackflow/config`
- `@stackflow/core`
- `@stackflow/react`
- `@stackflow/plugin-history-sync`
- (Optional) `@stackflow/link`
The Future API may have partial changes in the API. If you find any discrepancies with the documentation while using it, please ensure you are using the latest version.
#
# Loader API
The Loader API is built-in by default. Use it as shown below.
```tsx showLineNumbers filename="HomeActivity.loader.ts" copy
import { ActivityLoaderArgs, useLoaderData } from "@stackflow/react/future";
export function homeActivityLoader({ params }: ActivityLoaderArgs<"HomeActivity">) {
return {
// ...
}
}
```
Automatically filled with types.
```tsx showLineNumbers filename="HomeActivity.tsx" copy {4}
import { homeActivityLoader } from "./HomeActivity.loader";
export const HomeActivity: ActivityComponentType<"HomeActivity"> = () => {
const loaderData = useLoaderData();
}
```
In the `stackflow.config.ts` file, insert the created loader.
```tsx showLineNumbers filename="stackflow.config.ts" copy {9}
import { defineConfig } from "@stackflow/config";
import { homeActivityLoader } from "../components/HomeActivity.loader";
export const config = defineConfig({
activities: [
{
name: "HomeActivity",
route: "/",
loader: homeActivityLoader,
},
{
name: "MyProfileActivity",
route: "/my-profile",
}
],
transitionDuration: 270,
});
```
#
import { APITable } from '../../../components/APITable'
# `@stackflow/link`
It mimics the `` component behavior provided by [Gatsby](https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-link/) or [Next.js](https://nextjs.org/docs/app/api-reference/components/link).
## Dependencies
It can be used only when both [`@stackflow/plugin-history-sync`](/api-references/plugins/plugin-history-sync) and [`@stackflow/plugin-preload`](/api-references/plugins/plugin-preload) are set.
## Installation
```bash npm2yarn copy
npm install @stackflow/link
```
## Usage
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { historySyncPlugin } from "@stackflow/plugin-history-sync";
import { preloadPlugin } from "@stackflow/plugin-preload";
const { Stack, useFlow, activities } = stackflow({
activities: {
// ...
},
plugins: [
historySyncPlugin({
//...
}),
preloadPlugin({
// ...
}),
// ...
],
});
export type TypeActivities = typeof activities;
```
```tsx showLineNumbers filename="Link.tsx" copy
import { createLinkComponent } from "@stackflow/link";
import type { TypeActivities } from "./stackflow";
export const { Link } = createLinkComponent();
```
```tsx showLineNumbers filename="MyComponent.tsx" copy
import { Link } from './Link'
const MyComponent = () => {
return (
{/* ... */}
)
}
```
## Reference
| | | |
| ----------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| activityName | `string` | The name of the activity you want to link to. It's used to determine which route to navigate. |
| activityParams | `object` | Parameters to be passed to the activity. These parameters will be used to fill the route pattern. |
| animate | `boolean` (optional) | Indicates whether to animate the transition when navigating. If not provided, it defaults to no animation. |
| replace | `boolean` (optional) | If true, replaces the current entry in the history stack instead of pushing a new entry. |
| urlPatternOptions | `UrlPatternOptions` (optional) | Options to customize URL pattern matching and filling. |
| ref | `React.ForwardedRef` (optional) | A reference to the underlying anchor element, allowing direct DOM access if needed. |
| onClick | `function` (optional) | Function to handle the click event on the link. You can use it to perform additional actions on link clicks. |
| children | `React.ReactNode` | The content to be rendered inside the link. This is typically text or other elements the user can interact with. |
#
import { APITable } from "../../../components/APITable";
# `@stackflow/plugin-basic-ui`
Render the UI within the activity using the global stack state. It provides `cupertino` and `android` themes by default.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-basic-ui
```
## Usage
Provides components in the form of application app bars.
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
const { Stack, useFlow } = stackflow({
// ...
plugins: [
// ...
basicUIPlugin({
theme: "cupertino",
}),
],
});
```
### `basicUIPlugin` Options
| | | |
| ---- | ---- | ---- |
| theme | `cupertino` \| `android` | Set the theme. |
| rootClassName | `string`(optional) | Set the root class name. |
| appBar | `AppBar`(optional) | Set the app bar. |
```tsx filename="AppScreen" copy
import { AppScreen } from "@stackflow/plugin-basic-ui";
const Something = () => {
return (
Hello, World
);
};
```
### `appBar`
| | | |
| ---- | ---- | ---- |
| backButton | `{ renderIcon?: () => ReactNode; ariaLabel?: string }` \| `{ render?: () => ReactNode }` | Set the back button. |
| closeButton | `{ renderIcon?: () => ReactNode; ariaLabel?; string, onClick: (e) => void; }` \| `{ render?: () => ReactNode }` | Set the close button. |
It also provides modal and bottom sheet components.
```tsx filename="Modal" copy
import { Modal } from "@stackflow/plugin-basic-ui";
const Something = () => {
return (
);
};
```
#
# `@stackflow/plugin-devtools`
This plugin is used as a data storage layer for the Stackflow Devtools.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-devtools
```
## Usage
To utilize the plugin, integrate it within your Stackflow setup as shown:
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { devtoolsPlugin } from "@stackflow/plugin-devtools";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [
devtoolsPlugin(),
// ...
],
});
```
#
import { Callout } from "nextra-theme-docs";
# `@stackflow/plugin-google-analytics-4`
This plugin is used to integrate Google Analytics 4 with Stackflow.
## Inatallation
```bash npm2yarn copy
npm install @stackflow/plugin-google-analytics-4
```
## Usage
### Initialize
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { googleAnalyticsPlugin } from "@stackflow/plugin-google-analytics-4";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [
googleAnalyticsPlugin({
trackingId: "G-XXXXXXXXXX", // Required. Your Google Analytics 4 Tracking ID
userInfo: {
// optional
userId: "test123", // Your own user distinguishable id. (https://bit.ly/3VGu04K)
userProperties: {
// ...
// You can add additional event parameters. This value will collected as a user properties of "Custom Dimension" in GA.
// https://bit.ly/3uQbriR
},
},
useTitle: true, // Optional. If true, the title of the current screen will be sent to GA. if false, ActivityName will be sent to GA.(default false).
}),
],
});
```
### Set config
```tsx showLineNumbers filename="App.tsx" copy
import { useGoogleAnalyticsContext } from "@stackflow/plugin-google-analytics-4";
const App = () => {
const { setConfig } = useGoogleAnalyticsContext();
useEffect(() => {
setConfig({
user_id: "test123",
user_properties: {
// ...
},
// ...
// GA4 config values.
// https://bit.ly/3Y7IXhV
});
}, []);
return
...
;
};
```
### send event
Every stack has wrapped as a context, you can use `useGoogleAnalyticsContext` hook to send event.
```tsx showLineNumbers filename="AdCreateButton.tsx" copy
import { useGoogleAnalyticsContext } from "@stackflow/plugin-google-analytics-4";
// ...
const { sendEvent } = useGoogleAnalyticsContext();
return (
<>
>
);
```
Here's an example capture of custom GA4 event sent from the above code.
Note that the second parameter object is sent as a custom event parameter.

FAQ: **Pageview event is triggered twice.**
Unckeck "Page changes based on browser history events" in GA4 settings (**_Web Stream>Enhanced Measurement>Pageviews>Advanced_**)
This plugin trigger pageview event manually using stackflow's "[Effect Hook](/docs/advanced/write-plugin#effect-hooks)". You don't have to trigger it again.

#
import { APITable } from "../../../components/APITable";
# `@stackflow/plugin-history-sync`
Stackflow does not use the browser's history by default. This plugin synchronizes the stack state with the current browser's history.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-history-sync
```
## Usage
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { historySyncPlugin } from "@stackflow/plugin-history-sync";
import { MyHome } from "./MyHome";
import { MyArticle } from "./MyArticle";
import { NotFoundPage } from "./NotFoundPage";
const { Stack, useFlow } = stackflow({
activities: {
MyHome,
MyArticle,
NotFoundPage,
},
plugins: [
// ...
historySyncPlugin({
routes: {
/**
* You can link the registered activity with the URL template.
*/
MyHome: "/",
MyArticle: "/articles/:articleId",
NotFoundPage: "/404",
},
/**
* If a URL that does not correspond to the URL template is given, it moves to the `fallbackActivity`.
*/
fallbackActivity: ({ context }) => "NotFoundPage",
/**
* Uses the hash portion of the URL (i.e. window.location.hash)
*/
useHash: false,
}),
],
});
```
## Reference
| | | |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| routes | `object` | Connects activities with URL templates. You can represent activity parameters as Path Parameters. If an activity's parameter is not in the URL template, it will automatically be represented as a Query Parameter. |
| fallbackActivity | `(args: { initialContext: any }) => K` | Determines which activity to navigate to if there is no matching URL when first entering. Typically, you create a 404 page and assign it here. |
| useHash | `boolean` (optional) | Determines if hash-based routing should be used. Defaults to false. |
| history | `History` (optional) | A custom history object used for managing navigation state. Defaults to browser or memory history. |
| urlPatternOptions | `UrlPatternOptions` (optional) | Options for URL pattern matching and generation, affecting how URLs are constructed and parsed. |
#
import { APITable } from "../../../components/APITable";
# `@stackflow/plugin-map-initial-activity`
This plugin is used to map the initial activity using the given URL.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-map-initial-activity
```
## Usage
```ts
import { stackflow } from "@stackflow/react";
import { mapInitialActivityPlugin } from "@stackflow/plugin-map-initial-activity";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [
mapInitialActivityPlugin({
mapper(url) {
// implement mapping logic using url parameter
return {
activityName: "...",
activityParams: {},
};
},
}),
],
});
```
## Reference
### Options
| | | |
| ----------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| mapper | `(url: URL) => { activityName: string; activityParams: {}; } \| null` | A function that takes a URL and returns an object with the activity name and parameters, or null if no match. |
#
import { APITable } from "../../../components/APITable";
# `@stackflow/plugin-preload`
`@stackflow/plugin-preload` is useful when you need to load remote data before rendering the activity.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-preload
```
## Usage
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { preloadPlugin } from "@stackflow/plugin-preload";
import { MyHome } from "./MyHome";
import { MyArticle } from "./MyArticle";
import { NotFoundPage } from "./NotFoundPage";
const { Stack, useFlow, activities } = stackflow({
activities: {
MyHome,
MyArticle,
NotFoundPage,
},
plugins: [
// ...
preloadPlugin({
loaders: {
MyHome({ activityParams }) {
// implement your own preload function using activity information
// when activity pushed, loader is automatically triggered before rendering
},
MyArticle() {
// ...
},
NotFoundPage() {
// ...
},
},
}),
],
});
export type TypeActivities = typeof activities;
```
```ts showLineNumbers filename="usePreloader.ts" copy {4}
import { createPreloader } from "@stackflow/plugin-preload";
import type { TypeActivities } from "./stackflow";
export const { usePreloader } = createPreloader();
```
```tsx showLineNumbers filename="MyComponent.tsx" copy {8}
import { usePreloader } from "./usePreloader";
const MyComponent = () => {
const { preload } = usePreloader();
useEffect(() => {
// imperatively preload
preload("MyArticle", {
/* ... */
});
}, []);
return
{/* ... */}
;
};
```
## Reference
### `preloadPlugin`
| | | |
| ------ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| loaders| `{ [key]: Loader }` | A mapping of activity names to their respective loader functions, defining how to preload the activity's data or resources. |
### `usePreloader`
| | | |
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| urlPatternOptions | `UrlPatternOptions` | Options for customizing URL pattern matching within the preloader function. |
#
# `@stackflow/plugin-renderer-basic`
This plugin is used to render the activity that should be rendered by default using the stack state.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-renderer-basic
```
## Usage
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [basicRendererPlugin()],
});
```
#
# `@stackflow/plugin-renderer-web`
Render active activity only using the stack state. this plugins can be used for web application to be served on the web browser
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-renderer-web
```
## Usage
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { webRendererPlugin } from "@stackflow/plugin-renderer-web";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [webRendererPlugin()],
});
```
#
import { APITable } from '../../../components/APITable';
# `@stackflow/plugin-stack-depth-change`
This plugin is useful when you want to monitor the depth of the stack.
## Installation
```bash npm2yarn copy
npm install @stackflow/plugin-stack-depth-change
```
## Usage
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { stackDepthChangePlugin } from "@stackflow/plugin-stack-depth-change";
const { Stack, useFlow } = stackflow({
activities: {
// ...
},
plugins: [
// ...
stackDepthChangePlugin({
onInit: ({ depth, activities, activeActivities }) => {},
onDepthChanged: ({ depth, activities, activeActivities }) => {},
}),
],
});
```
## Reference
### `StackDepthChangePluginOptions`
| | | |
| --------------- | ---------------------------------- | -------------------------------------------------------------------------- |
| onInit | `(args: StackDepthChangePluginArgs) => void` (optional) | Callback function to be invoked during plugin initialization with the current stack depth and activities. |
| onDepthChanged | `(args: StackDepthChangePluginArgs) => void` (optional) | Callback function to be triggered whenever there is a change in the stack depth. |
### `StackDepthChangePluginArgs`
| | | |
| --------------- | ---------------------------------- | -------------------------------------------------------------------------- |
| depth | `number` | The current depth of the active activities stack. |
| activities | `Activity[]` | An array of all activities currently in the stack. |
| activeActivities| `Activity[]` | An array of activities that are active (either "exit-active" or "enter-done"). |
#
import { Callout } from "nextra-theme-docs";
import { APITable } from "../../../components/APITable";
# Synchronizing with History
**Stackflow**'s navigation logic does not rely on browser history by default. This is to support environments like React Native and NativeScript where the History API is not available. Therefore, to use browser history for navigation, you need to synchronize the stack state with the browser history. This functionality is provided by `@stackflow/plugin-history-sync`.
Install `@stackflow/plugin-history-sync` using the following command.
```sh npm2yarn copy
npm install @stackflow/plugin-history-sync
```
Once the installation is complete, register the plugin in the `plugins` field of the `stackflow()` function as follows.
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { historySyncPlugin } from "@stackflow/plugin-history-sync";
import MyActivity from "./MyActivity";
import Article from "./Article";
const { Stack, useFlow } = stackflow({
transitionDuration: 350,
activities: {
MyActivity,
Article,
},
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
historySyncPlugin({
routes: {
MyActivity: "/my-activity",
Article: "/articles/:articleId",
},
fallbackActivity: () => "MyActivity",
}),
],
// The initialActivity option no longer works because it is overridden by the historySyncPlugin.
// initialActivity: () => "MyActivity",
});
```
The `historySyncPlugin` accepts two options: `routes` and `fallbackActivity`.
| | | |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| routes | `object` | Connects activities with URL templates. You can represent activity parameters as Path Parameters. If an activity's parameter is not in the URL template, it will automatically be represented as a Query Parameter. |
| fallbackActivity | `function` | Determines which activity to navigate to if there is no matching URL when first entering. Typically, you create a 404 page and assign it here. |
**Warning** - When mapping activity parameters to path parameters, ensure that the parameter values are URL-safe. If special characters that are not URL-safe are used, query parameters may appear duplicated.
In a server-side rendering environment, the `window.location` value is not available, so the initial activity cannot be determined. To set the initial activity, add the path value to the `req.path` field in the `initialContext` of the Stack as follows:
```tsx
```
#
import { Callout } from "nextra-theme-docs";
# Preloading API Calls
Using the [Future API](/api-references/future-api/introduction) allows you to implement preloading more cleanly by leveraging the built-in Loader API. We recommend using the [Future API](/api-references/future-api/introduction).
## Preload Plugin
The Preload Plugin allows you to fetch the data required for an activity in advance.
Install `@stackflow/plugin-preload` with the following command:
```bash npm2yarn copy
npm install @stackflow/plugin-preload
```
Once the installation is complete, register the plugin in the `plugins` field of the `stackflow()` function as follows.
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { preloadPlugin } from "@stackflow/plugin-preload";
import MyActivity from "./MyActivity";
import Article from "./Article";
const { Stack, useFlow, activities } = stackflow({
transitionDuration: 350,
activities: {
MyActivity,
Article,
},
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
preloadPlugin({
loaders: {
MyActivity({ activityName, activityParams }) {
// Fetch the data required for the activity using the activity name and parameters.
// Return a reference value that allows access to the data,
// which can be retrieved using the `useActivityPreloadRef` hook.
return preloadRef;
},
},
}),
],
});
export type TypeActivities = typeof activities;
```
Afterwards, within each activity, you can retrieve the `preloadRef` value using the `useActivityPreloadRef()` hook.
```tsx showLineNumbers filename="MyActivity.ts" copy
import type { ActivityComponentType } from "@stackflow/react";
import { useActivityPreloadRef } from "@stackflow/plugin-preload";
const MyActivity: ActivityComponentType = () => {
const preloadRef = useActivityPreloadRef();
// Implement directly using Suspense for Data Fetching.
// https://ko.reactjs.org/docs/concurrent-mode-suspense.html
const data = readData({ preloadRef });
return
{/* ... */}
;
};
```
If you want to preload the API for the next screen at a specific point in time, you can use the `preload` function provided by the `usePreloader()` hook from `@stackflow/plugin-preload`.
Create a type-safe `usePreloader()` hook using the `createPreloader()` function.
```tsx showLineNumbers filename="usePreloader.ts" copy
import { createPreloader } from "@stackflow/plugin-preload";
import type { TypeActivities } from "./stackflow";
// Pass the entire type of registered activities as a generic to use the usePreloader() hook in a type-safe manner
export const { usePreloader } = createPreloader();
```
```tsx showLineNumbers filename="MyActivity.ts" copy
import type { ActivityComponentType } from "@stackflow/react";
import { usePreloader } from "./usePreloader";
const MyActivity: = () => {
const { preload } = usePreloader();
useEffect(() => {
// Fetch the data required by the `Article` component when rendering the `MyActivity` component.
preload("Article", {
/* ... */
});
}, []);
return
{/* ... */}
;
};
```
### The `` Component
If your project uses both the Preload Plugin and the History Sync Plugin, you can utilize the `` component.
Install `@stackflow/link` with the following command:
```bash npm2yarn copy
npm install @stackflow/link
```
Create a type-safe Link component using the `createLinkComponent()` function.
```tsx showLineNumbers filename="Link.ts" copy
import { createLinkComponent } from "@stackflow/link";
import type { TypeActivities } from "./stackflow";
// Similarly, pass the entire type of activities as a generic to use the component in a type-safe manner
export const { Link } = createLinkComponent();
```
Afterwards, use the `` component with `activityName` and `activityParams` as props as follows.
```tsx showLineNumbers filename="MyComponent.tsx" copy
import { Link } from './Link'
const MyComponent = () => {
return (
{/* ... */}
)
}
```
The `` component internally uses the `usePreloader()` hook to fetch data in advance when the component is exposed within the user's viewport. This provides a seamless exploration experience without loading times for the user.
#
import { Callout } from "nextra-theme-docs";
# Resolving Circular References with useFlow
You can easily resolve the `useFlow()` circular reference issue by using the [Future API](/api-references/future-api/introduction). We recommend using the [Future API](/api-references/future-api/introduction).
The `useFlow()` function returned by the `stackflow()` function utilizes the declared activity types. Therefore, `useFlow()` and activity components interlock, causing a circular dependency. By using the `useActions()` hook and importing the types separately, you can eliminate this circular dependency.
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
export const { Stack, activities } = stackflow({
activities: {
// ...
},
// ...
});
// Expose the type of activities like this.
export type TypeActivities = typeof activities;
```
```tsx showLineNumbers filename="stackflow.ts" copy
import { useActions } from "@stackflow/react";
// Only import the exposed activity type.
import type { TypeActivities } from "./stackflow";
export const useMyFlow = () => {
return useActions();
};
```
`TypeActivities` will be similarly utilized in future utility components/hooks.
#
import { Callout } from "nextra-theme-docs";
import { EffectHookDiagram } from "../../../components/diagrams/EffectHookDiagram";
import { APITable } from "../../../components/APITable";
# Write Your Own Plugin
**Stackflow** helps you easily integrate extension logic written by others into your application through the plugin interface. Solve your problems with plugins and wrap them nicely to share with others.
## Making a Preset
The easiest way to publish a **Stackflow** plugin is to provide it as a preset by combining plugins made by others. You can create your own preset by grouping multiple plugins into an array as shown below.
```ts showLineNumbers filename="stackflow.ts" copy
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { historySyncPlugin } from "@stackflow/plugin-history-sync";
import { stackflow } from "@stackflow/react";
const myPlugin = ({ ... }) => [
basicRendererPlugin(),
historySyncPlugin({ ... }),
];
stackflow({
// ...
plugins: [myPlugin()],
});
```
## Basic Interface
Plugin must return the following values as a function.
| | | |
| ---- | -------- | ---------------------------------------------------------------------------------------------------------- |
| key | `string` | A unique string value assigned as the `key` when the plugin is rendered within the React tree as an array. |
To try developing your first plugin for your app, start with an inline function as shown below.
```ts showLineNumbers filename="stackflow.ts" copy
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { stackflow } from "@stackflow/react";
stackflow({
// ...
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
() => {
return {
key: "my-plugin",
};
},
],
});
```
### Adding a render method
If you want to add a new rendering, you can use the `render` API. Utilize the stack state to decide how to optimize the DOM or, if necessary, overlay a new DOM tree using the `render` API.
```tsx showLineNumbers filename="stackflow.ts" copy
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
render({ stack }) {
return (
{stack.render().activities.map((activity) => (
{activity.render()}
))}
);
},
};
},
],
});
```
You can also override the stack state passed to the UI as shown below.
```ts showLineNumbers filename="stackflow.ts" copy {12,17}
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
render({ stack }) {
return (
{stack
.render({
// You can override the stack state here.
})
.activities.map((activity) => (
{activity.render({
// You can override the activity state here.
})}
))}
);
},
};
},
],
});
```
The overridden state value does not affect the main stack, but only applies to the `useStack()` and `useActivity()` within the rendering subtree of React.
### Wrapping Stack
If you want to add a Context API Provider at the top level or wrap the top level with a specific DOM, use the `wrapStack` interface.
```ts showLineNumbers filename="stackflow.ts" copy {8}
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
wrapStack({ stack }) {
// you can use the stack information brought in as an argument here
return
{stack.render()}
;
},
};
},
],
});
```
**Caution** - The `wrapStack` API is applied to all renderings. Be careful as unintended side effects may occur.
### Wrapping Activity
If you want to add a Context API Provider to each activity or wrap it with a specific DOM, use the `wrapActivity` interface.
```ts showLineNumbers filename="stackflow.ts" copy {8}
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
wrapActivity({ activity }) {
// you can use the activity information brought in as an argument here
return
{activity.render()}
;
},
};
},
],
});
```
**Caution** - The `wrapActivity` API is applied to all renderings. Be careful as unintended side effects may occur.
### Injecting Behavior at Initialization
Use the `onInit` hook to call logic when the `` component is first initialized.
```ts showLineNumbers filename="stackflow.ts" copy {7-9}
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
onInit() {
console.log("Initialized!");
},
};
},
],
});
```
**Caution** - In React 18 and `React.StrictMode`, it may be called twice, so be careful to avoid unintended side effects.
## Effect Hooks
Do you want to extend functionality or synchronize with external states? You can perform specific actions whenever the stack state changes or call functions before the stack state changes.
The stack state begins to change when push, replace, or pop is called due to user actions. From that point, you can perform specific actions before the stack state changes (Pre-effect) and after the stack state has changed and the UI has been updated (Post-effect).
### Post-effects
Post-effect hooks include `onPushed`, `onReplaced`, `onPopped`, and `onChanged`. Post-effect hooks are called after the UI has been fully updated, so you cannot undo or cancel the changes. Post-effect hooks can use the following arguments.
| | | |
| --------------------- | ---------- | ------------------------------------------ |
| actions.getStack | `function` | Get the current stack state. |
| actions.dispatchEvent | `function` | Add a new event to the core. |
| effect | `object` | The effect that triggered the effect hook. |
```ts showLineNumbers filename="stackflow.ts" copy
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
onPushed(actions, effect) {
// you can utilize
// actions.getStack()
// actions.dispatchEvent(...)
console.log("Pushed!");
console.log("Effect:", effect);
},
};
},
],
});
```
The `onChanged` hook is called whenever the stack state changes without distinction. Therefore, if used together with `onPushed`, `onReplaced`, and `onPopped`, both effect hooks will be called.
**Caution** - If you dispatch an event that triggers the effect within the effect hook, it can cause an infinite loop. Use `actions.dispatchEvent()` with caution.
### Pre-effects
Pre-effect hooks include `onBeforePush`, `onBeforeReplace`, and `onBeforePop`. Pre-effect hooks are called before the event is delivered to the Core, allowing you to cancel the event. Pre-effect hooks can use the following arguments.
| | | |
| ---------------------- | ---------- | ------------------------------------------ |
| actions.preventDefault | `function` | Cancel the default behavior. |
| actions.getStack | `function` | Get the current stack state. |
| actions.dispatchEvent | `function` | Add a new event to the core. |
| effect | `object` | The effect that triggered the effect hook. |
## Determining initial activity
You can override the existing `initialActivity` behavior through the `initialPushedEvent` API.
```ts showLineNumbers filename="stackflow.ts" copy {1, 9-13}
import { makeEvent } from "@stackflow/core";
stackflow({
// ...
plugins: [
() => {
return {
key: "my-plugin",
initialPushedEvent() {
return makeEvent("Pushed", {
// ...
});
},
};
},
],
});
```
# LLMs.txt
# LLMs.txt
We provide an LLMs.txt setup to help large language models (LLMs) easily understand Stackflow.
### Structure
We provide the following LLMs.txt files:
- [llms.txt](https://stackflow.so/llms.txt): The main file that describes the structure of all LLMs.txt files.
- [llms-full.txt](https://stackflow.so/llms-full.txt): Includes all Stackflow documentation.
- [llms-changelog.txt](https://stackflow.so/llms-changelog.txt): Contains the latest updates and changes so you can review version-by-version history.
### Using with AI tools
#### Cursor
You can include the LLMs.txt files in your project using Cursor's @Docs feature.
[**Learn more about Cursor @Docs**](https://docs.cursor.com/context/@-symbols/@-docs)
# Changelog
import ChangelogContent from "../../components/ChangelogContent.mdx"
#
import { Callout } from "nextra-theme-docs";
import { ActivityDiagram } from "../../../components/diagrams/ActivityDiagram";
import { APITable } from "../../../components/APITable";
# Activity
**Activity** is a single screen that gets stacked one by one. Activities have the following properties and can be accessed using the `useActivity()` hook if needed.
| | | |
| --------------- | -------------------------------------------------------- | -------------------------------------------------------- |
| id | `string` | Unique ID value for each activated activity screen |
| name | `string` | Registerd activity name |
| transitionState | `enter-active`, `enter-done`, `exit-active`, `exit-done` | Transition state of current activity |
## Registering an Activity
To actually use an activity, you need to register it with the `stackflow()` function before using it. An activity is a React component declared with the type `ActivityComponentType`.
```tsx showLineNumbers filename="MyActivity.tsx" copy
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
const MyActivity: ActivityComponentType = () => {
return (
My Activity
);
};
export default MyActivity;
```
`ActivityComponentType` is compatible with `React.ComponentType`. Therefore, you can continue to use `React.FC`, `React.Component`, etc., as you have been.
**Stackflow** does not provide a default UI. Instead, it offers basic iOS (`cupertino`) and Android (`android`) UIs through the `@stackflow/plugin-basic-ui`.
If you have declared the activity correctly, register it in the `activities` field of the `stackflow()` function as follows.
```tsx showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
import MyActivity from "./MyActivity";
export const { Stack, useFlow } = stackflow({
transitionDuration: 350,
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
],
activities: {
MyActivity,
},
});
```
## Registering initial Activity
Have you successfully registered the activity? However, the `` component that you initialized earlier might not be rendering anything. This is because you haven't set an initial activity. If you want to load a specific activity initially, add the `initialActivity` option as follows.
```ts showLineNumbers {16} filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
import MyActivity from "./MyActivity";
export const { Stack, useFlow } = stackflow({
transitionDuration: 350,
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
],
activities: {
MyActivity,
},
initialActivity: () => "MyActivity",
});
```
If you have successfully registered the initial activity, you can see the rendered result on the screen.
Have you experienced the auto-completion of the `MyActivity` value in TypeScript?
**Stackflow** will help improve your development productivity through such auto-completion experiences.
## Registering Activity with Parameters
Some activities require specific parameters when used. In such cases, declare the parameter as the activity's Props.
```tsx showLineNumbers filename="Article.tsx" copy
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
type ArticleParams = {
title: string;
};
const Article: ActivityComponentType = ({ params }) => {
return (
);
};
export default Article;
```
**Caution** - If the required parameters are not passed from the previous screen, a critical error may occur.
**Warning** - Initial activity must not require parameters.
---
Have you successfully registered the activity? Now, let's learn how to open the registered activity and navigate between them.
# Getting State
import { TransitioningStackDiagram } from "../../../components/diagrams/TransitioningStackDiagram";
import { APITable } from "../../../components/APITable";
# Getting State
The internal state of **Stackflow** can be described in one word: a **stack** data structure with **transition states**.
The activities accessible through the `activities` field contain information related to their basic existence, such as ID, name, and the current transition state. These state values are utilized in various ways to create the `@stackflow/plugin-basic-ui`. (You can create one too!)
## Utilizing Stack State in Rendering
To access the stack state in a UI component, use the `useStack()` hook.
```tsx showLineNumbers filename="MyActivity.tsx" copy
import { useStack, type ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";
const MyActivity: ActivityComponentType = () => {
const stack = useStack();
const { replace } = useFlow();
const onClick = () => {
replace("Article", {
title: "Hello",
});
};
useEffect(() => {
console.log("Stacked Activities:", stack.activities);
console.log("Current Transition State:", stack.globalTransitionState);
console.log(
"Initial Transition Duration Options",
stack.transitionDuration,
);
}, [stack]);
return (
My Activity
);
};
export default MyActivity;
```
There are the following fields in the stack state.
| | | |
| --------------------- | ----------------- | ----------------------------------------------- |
| activities | `Activity[]` | list of activites |
| transitionDuration | `number` | `transitionDuration` value set in `stackflow()` |
| globalTransitionState | `idle`, `loading` | if current activity is animating or not |
## Utilizing Activity State in Rendering
You can use the `useActivity()` hook to get information about the current activity.
```tsx showLineNumbers filename="MyActivity.tsx" copy
import { useActivity, type ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";
const MyActivity: ActivityComponentType = () => {
const activity = useActivity();
const { replace } = useFlow();
const onClick = () => {
replace("Article", {
title: "Hello",
});
};
useEffect(() => {
console.log("Transition State of Current Activity:", activity.transitionState);
}, [activity]);
return (
My Activity
);
};
export default MyActivity;
```
The fields in the activity state are as follows.
| | | |
| ------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------- |
| id | `string` | Activity ID |
| name | `string` | Registered activity name |
| transitionState | `enter-active`, `enter-done`, `exit-active`, `exit-done` | Transition state of the activity |
| params | `Object` | Parameters passed to the activity |
| isActive | `boolean` | Whether is active activity (`false` when `transitionState` is `exit-active`) |
| isTop | `boolean` | Whether is top activity (`true` when `transitionState` is `exit-active`) |
| isRoot | `boolean` | Whether is root activity |
## Customize UI
You can freely customize the UI by using states such as `useActivity()` and `useStack()` in the desired component.
If you want to utilize the UI provided by `@stackflow/plugin-basic-ui`, use the provided `AppScreen` component.
---
Do you want to extend the UI or logic and share it with other developers? Let's move on to learn how to create a plugin.
#
import { Steps } from "nextra/components";
# Installation
## Install Stackflow
### Installation
Install Stackflow in your React project with the following command.
```sh npm2yarn copy
npm install @stackflow/core @stackflow/react
```
### Initialize Stackflow
Create a JavaScript (or TypeScript) file in your project and call the `stackflow()` function to generate the `` and `useFlow()` functions.
And export them so that `` and `useFlow()` can be used in other components.
```ts showLineNumbers filename="stackflow.ts" copy
import { stackflow } from "@stackflow/react";
export const { Stack, useFlow } = stackflow({
transitionDuration: 350,
activities: {},
plugins: [],
});
```
## Extend with Basic UI
### Installation
**Stackflow** does not include UI (DOM and CSS) implementation by default. To achieve the desired rendering results, you need to add plugins. Install the `@stackflow/plugin-renderer-basic` plugin and the `@stackflow/plugin-basic-ui` extension with the following command.
```sh npm2yarn copy
npm install @stackflow/plugin-renderer-basic @stackflow/plugin-basic-ui
```
### Initialize UI Plugins
Initialize the `basicRendererPlugin()` from `@stackflow/plugin-renderer-basic` and the `basicUIPlugin()` from `@stackflow/plugin-basic-ui` in the `plugins` field of the `stackflow()` function as follows.
```ts showLineNumbers filename="stackflow.ts" copy {8-13}
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
export const { Stack, useFlow } = stackflow({
transitionDuration: 350,
activities: {},
plugins: [
basicRendererPlugin(),
basicUIPlugin({
theme: "cupertino",
}),
],
});
```
### Include CSS
Also, include the CSS provided by `@stackflow/plugin-basic-ui` somewhere in your code.
```ts copy
import "@stackflow/plugin-basic-ui/index.css";
```
### Render the Stack Component
And initialize the `` component at the desired rendering location as follows.
```tsx showLineNumbers filename="App.tsx" copy
import { Stack } from "./stackflow";
const App = () => {
return (
);
};
export default App;
```
---
If you have completed up to this point, let's move on to learn how to register activities.
# Introduction
import { Callout } from "nextra-theme-docs";
import { Demo } from "../../../components/Demo";
import { CurrentDate } from "../../../components/CurrentDate";
# Introduction
**Stackflow** is a project that implements Stack Navigation UX, commonly used in mobile devices (iOS/Android, etc.), in a JavaScript environment, making it easier to develop hybrid apps and webviews.
- Stacks screens and maintains scroll position.
- Supports transition effects for stacking screens and disappearing screens when navigating back.
- Supports iOS-style swipe-back gestures for navigating back.
- Passes necessary parameters to the transitioning screen.
### Customization
You can use the stack and transition state without any UI, allowing you to customize the UI as you like.
You can inject desired extensions into the lifecycle through the plugin interface.
### Integration with Various Platforms
The core logic and integration layer are separated, allowing integration with various frontend frameworks.
You can inject render logic and UI externally, enabling simultaneous development of mobile webviews and desktop applications from a single codebase.
### Server-Side Rendering, TypeScript
Supports `ReactDOMServer.renderToString`. stackflow provides type definitions for all functions.
As of , it supports React and React DOM as references.
# Navigating Activities
import { Callout } from "nextra-theme-docs";
import { APITable } from "../../../components/APITable";
# Navigating Activities
If you have successfully registered an activity, it's time to navigate between activities. **Stackflow** supports stacking, replacing, and deleting activities through `useFlow()`. Let's take a look!
## Stacking a New Activity
We use the `useFlow()` hook created in `stackflow.ts`. Through the `push()` function within this hook, we can stack a new activity as follows.
```tsx showLineNumbers filename="MyActivity.tsx" copy /push/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";
const MyActivity: ActivityComponentType = () => {
const { push } = useFlow();
const onClick = () => {
push("Article", {
title: "Hello",
});
};
return (
My Activity
);
};
export default MyActivity;
```
`push()` takes the name of the activity to navigate to as the first parameter, the parameters for the activity as the second parameter, and additional options as the third parameter. The third parameter, additional options, is optional and can be omitted (default values will be used).
```ts
push("activity_name", {
/* activity parameters */
});
// or
push(
"activity_name",
{
/* activity parameters */
},
{
/* additional options */
},
);
```
The third parameter of the `push()` function, additional options, includes the following values.
| | | | |
| ------- | --------- | ------------------------ | ---- |
| animate | `boolean` | Turn on or off animation | true |
By utilizing TypeScript, you can ensure that activity names and parameters are strictly typed. Use TypeScript to safely and conveniently leverage **Stackflow**.
## Replacing the Current Activity
Next, let's look at how to replace the current activity without adding a new activity to the stack. Using the `replace()` function from the `useFlow()` hook created in `stackflow.ts`, you can replace the current activity as follows.
```tsx showLineNumbers filename="MyActivity.tsx" copy /replace/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";
const MyActivity: ActivityComponentType = () => {
const { replace } = useFlow();
const onClick = () => {
replace("Article", {
title: "Hello",
});
};
return (
My Activity
);
};
export default MyActivity;
```
`replace()` has a similar API to `push()`. It takes the name of the activity to navigate to as the first parameter, the parameters for the activity as the second parameter, and additional options as the third parameter. The third parameter, additional options, is optional and can be omitted (default values will be used).
```ts
replace("activity_name", {
/* activity parameters */
});
// or
replace(
"activity_name",
{
/* activity parameters */
},
{
/* additional options */
},
);
```
The third parameter of the `replace()` function, additional options, includes the following values.
| | | | |
| ------- | --------- | ------------------------ | ---- |
| animate | `boolean` | Turn on or off animation | true |
## Deleting the Current Activity
Finally, let's look at how to delete the current activity and return to the previous activity. Using the `pop()` function from the `useFlow()` hook created in `stackflow.ts`, you can delete the current activity as follows.
```tsx showLineNumbers filename="Article.tsx" copy /pop/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";
type ArticleParams = {
title: string;
};
const Article: ActivityComponentType = ({ params }) => {
const { pop } = useFlow();
const goBack = () => {
// Pop a single activity
pop();
};
const goBackMultiple = () => {
// Pop multiple activities
pop(3);
};
return (
{params.title}
);
};
export default Article;
```
`pop()` takes optional parameters for the number of activities to pop and additional options. These parameters can be omitted, and default values will be used.
```ts
pop(); // pop a single activity
pop(3); // pop multiple activities
pop({
/* additional option */
}); // pop a single activity with additional options
pop(3, {
/* additional option */
}); // pop multiple activities with additional options
```
The first parameter of the pop() function can specify the number of activities to pop or define additional options. If the first parameter is used for the number of activities, the second parameter can then be used to provide additional options.
The additional options include the following values.
| | | | |
| ------- | --------- | ------------------------ | ---- |
| animate | `boolean` | Turn on or off animation | true |
---
We have learned how to stack, replace, and delete activities. Now, let's learn how to create a virtual stack within an activity.
# Navigating Step
import { StepDiagram } from "../../../components/diagrams/StepDiagram";
import { Callout, Link } from "nextra-theme-docs";
# Navigating Step
You can use steps when you want to have a virtual stack state within a single activity. Steps work by changing the parameters of the activity.
`@stackflow/plugin-history-sync` supports steps. If you need to handle specific state manipulations along with Android back button support on mobile, using the step feature is better than `history.pushState()`.
## Stacking a New Step
Use the `useStepFlow()` hook created in `stackflow.ts`. Through the `stepPush()` function within this hook, you can stack a new step as follows.
```tsx showLineNumbers filename="Article.tsx" copy /stepPush/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "./stackflow";
type ArticleParams = {
title: string;
};
const Article: ActivityComponentType = ({ params }) => {
// For type safety, put the name of the current activity
const { stepPush } = useStepFlow("Article");
const onNextClick = () => {
// When you call `stepPush()`, `params.title` changes.
stepPush({
title: "Next Title",
});
};
return (
{params.title}
);
};
export default Article;
```
## Replacing a Step
You can replace the current step using the `stepReplace()` function in `useStepFlow()`.
```tsx showLineNumbers filename="Article.tsx" copy /stepReplace/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "./stackflow";
type ArticleParams = {
title: string;
};
const Article: ActivityComponentType = ({ params }) => {
// For type safety, put the name of the current activity
const { stepReplace } = useStepFlow("Article");
const onChangeClick = () => {
// When you call `stepReplace()`, the title changes to "Next Title".
stepReplace({
title: "Next Title",
});
};
return (
{params.title}
);
};
export default Article;
```
## Deleting a Step
You can delete the current step using the `stepPop()` function in `useStepFlow()`.
```tsx showLineNumbers filename="Article.tsx" copy /stepPop/
import type { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "./stackflow";
type ArticleParams = {
title: string;
};
const Article: ActivityComponentType = ({ params }) => {
// For type safety, put the name of the current activity
const { stepPop } = useStepFlow("Article");
const onPrevClick = () => {
// When you call `stepPop()`, the current step is deleted.
stepPop();
};
return (
{params.title}
);
};
export default Article;
```
If there's no step to delete, nothing happens when you call `stepPop()`.
If you use `useFlow().pop()` in a state where multiple steps have been pushed, all the steps stacked inside the activity are deleted at once.
---
You've learned the basics of using **Stackflow**. Now, let's go beyond using it and learn about the internal structure of the stack state and specific application methods using it.
# Stackflow β The Simplest Stack Navigation for JavaScript and TypeScript
import { IndexPage } from "../components/index-page";