W↓
All docs
🔑
Sign Up/Sign In
onestack.dev/docs/
Public Link
Apr 15, 2025, 4:38:00 PM - complete - 78.1 kB
Apr 15, 2025, 4:38:00 PM - complete - 78.1 kB
Apr 15, 2025, 4:37:42 PM - complete -
Created by:
****ad@vlad.studio
Starting URLs:
https://onestack.dev/docs/introduction
Crawl Prefixes:
https://onestack.dev/docs/
## Page: https://onestack.dev/docs/introduction ### One is a React framework that aims to make full stack development as simple as possible \- whether you're making a website, an app, or sharing code between the two. One lets you target both React Native and web with a single codebase, replacing both Metro and a separate web framework with a single framework, with shared file-system routing. One runs on Vite, which brings many benefits. ## Highlights * Target native and web at once with typed file system routes. * Loaders ala Remix that tree-shake away on the client. * Routing modes on a per-page basis (SSG, SSR, SPA, and API). * Preloads all JS for initial load and when hovering links, without need for a route map. * Includes a production server out of the box. ## Use Cases One works well as: * A uniquely good way to ship cross-platform apps with shared code. * A light, fast and opinionated framework for web-only or native-only. Keep in mind, One is in early development and needs time to stabilize. For a few more answers to common questions, see our FAQ. ## Our Full-Stack Philosophy One lets you customize your stack as you please but recommends Zero as the first-class solution to data. We think the client-side "sync engine" model it uses is a clear win-win for simpler code and better UX. Today, we have a starter with `npx one` that sets up an early alpha of Zero, while we work on a more official solution that neatly integrates One and Zero out of the box. ## Acknowledgements One started as an experiment in porting Expo Router to our vxrn library. We since changed much of the internals and added render modes and a variety of features. We owe the Expo team a huge thanks for their pioneering efforts. One was built thanks to some amazing open source technologies and contributors: * React Navigation provides the base routing library. * Expo Router for large parts of the initial One router. * Software Mansion for React Native Screens and React Native Gesture Handler. * Remix for inspiration for the `loader` APIs. * Vite, of course. And a special thanks to the following individuals: * Matias Capeletto for inviting us to speak at ViteConf, giving the initial spark. * Fatih Aygün for freely helping at length, getting us past many hurdles. * Hiroshi Ogawa for his expert consulting for which One wouldn't exist without. * Dan Maier, for graciously gifting us the `one` npm package. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/status ### One is currently in beta This is a live document covering the high level status of the various parts of One. We don't cover all areas, just high level stable, and all developing or unstable sub-areas. Broadly: web is generally stable, native is a bit further behind but is able to be daily driven in dev mode. We need to test and improve support of a wider variety of node\_modules, and add documentation and tests. wwwwwwwwwwwwwwwwwww #### Stages, from least to most stable * Early · Developing · Mostly Stable · Stable * Features in Developing or later are available to use. wwwwwwwwwwwwwwwwwww ### OS Support * Mac Mostly Stable * Linux Developing * Windows Early wwwwwwwwwwwwwwwwwww ### Web Mostly Stable Web is mostly production ready, with a few smaller features still in development and wider testing and feedback needed. * Local development Stable * Production builds Stable * Not Found Stable wwwwwwwwwwwwwwwwwww ### Native Developing Native local development works fairly well, but needs broader testing. Production builds have landed but have not been tested extensively. * Local development Developing * Production builds Developing \- Working but needs much wider testing. * Assets Developing * Symbolicator Early wwwwwwwwwwwwwwwwwww ### Routing Mostly Stable SSG and SPA are mostly stable, SSR just landed and needs a decent amount of work to be stable. * Performance Mostly Stable \- Needs to move to trie-based router, current loop-based. * SSG Mostly Stable * SPA Mostly Stable * SSR Developing wwwwwwwwwwwwwwwwwww ### Components * Slot Stable * Stack Stable * Tabs Stable * Drawer Early wwwwwwwwwwwwwwwwwww ### Data Early Zero is in public alpha. We have a starter available with `npx one` but Zero still needs to land a few more features before we recommend it for production. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/installation ### A quick tutorial from start to finish Getting started with One is as as easy as: `npx one` The CLI will guide you through the process of bootstrapping one of our starter templates. If this is your first One app, we recommend taking this route, as you can choose between bare-bones example all the way up to a full stack with a user system and authentication. We will be launching more starters as the project matures. ### 5-minute tutorial While we recommend using a preset to get started, it's helpful to walk through creating a new One app from scratch. For those of you who prefer to learn by doing, we've put together a 5-minute tutorial that will have you building a basic app in no time. Let's begin with our package.json: package.json It is strongly recommended to pin the `one` version for production stability: `npm install one@latest --save-exact` This ensures consistent behavior across different environments and deployments. If using pnpm or a package manager that symlinks dependencies, you'll want to disable that setting. One has (just a few) patches that fix dependencies. In part this is to enable switching between React 19 on web and React 18 on native, in part this is because the React Native ecosystem has many, many weirdly published packages - think JSX or Flowtype published in `.js` files, or non-standard legacy babel experimental transforms, etc. We are working with package authors to improve this, but we've found patches to be both simpler and require less mysterious behavior/slowdowns than the alternative of making Vite handle everything internally. ### Vite Config Next, let's add our ultra-simple `vite.config.ts`: vite.config.ts ### TSConfig One will create a TSConfig for you when you first start the dev server, with some defaults that work well. Of course you can create your own if you'd prefer. The default looks like this: ### File System Routes One expects your file system routes to be placed in an `app` directory. `app/_layout.tsx` serves as the top-level wrapper for all routes, ideal for: 1. Consistent page structure 2. Global styles or themes 3. Persistent navigation 4. App-wide state management One uses the `<Slot />` component to indicate where child components should render, allowing for nested layouts and fine-grained control over your UI structure. Let's create a root layout `app/_layout.tsx`: app/\_layout.tsx And then create a simple index page `app/index.tsx`: app/index.tsx You can now install your node modules and run your app. 🎉 `npm install && npm run dev` ### Running your app If the port isn't taken, you should be able to load http://localhost:8081 and to see your website (use `--port` to change the port). On native, you can use an Expo Go development app and load your app with exp://0.0.0.0:8081. Press the key combination q, r in your terminal which is running the dev server, and scan the QR code with your iPhone's camera if your computer and iPhone are connected to the same WiFi network. Note that for web-only projects you don't need `react-native`, and further note that if you want something that has a much broader styling featureset and much lighter bundle-size on web, you can use Tamagui, which has no dependency on React Native. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/configuration `import type { UserConfig } from 'vite'` `import { one } from 'one/vite'` `export default {` `plugins: [` `one({` `// all web-specific configuration nests in web:` `web: {` `// Choose the default strategy for routes without path-specifiers to render in` `// can be one of "spa", "ssg" or "ssr"` `defaultRenderMode: 'spa', // defaults to 'ssg'` `// If using a server-rendered render mode, looseHydration will collapse large hydration` `// error logs in development mode. React can gracefully recover from hydration issues,` `// and they can often be ignored, but by default React produces large error logs.` `// Turning this on will collapse those logs to one line.` `looseHydration: false, // defaults to true` `// Define server redirects for development and production` `redirects: [` `{` `source: '/vite',` `destination: 'https://vxrn.dev',` `permanent: true,` `},` `{` `source: '/docs/components/:slug/:version',` `destination: '/ui/:slug/:version',` `permanent: true,` `},` `],` `},` `/**` `* Path to a js or ts file to import before the rest of your app runs` `* One controls your root, but you may want to run some JS before anything else` `* Use this to give One the entrypoint to run` `*/` `setupFile: './setup.ts',` `optimization: {` `/**` `* Turn on [vite-plugin-barrel](https://github.com/JiangWeixian/vite-plugin-barrel/tree/master).` `* Optimizes barrel export files to speed up your build, you must list the packages that have` `* barrel exports. Especially useful for icon packs.` `*` `* @default ['@tamagui/lucide-icons']` `* @type boolean | string[]` `*/` `barrel: ['my-icon-package']` `},` `// native-specific config:` `native: {` `// One will set up your React Native app to run via AppRegistry.registerComponent(app.key)` `// This setting determines app.key and must match the React Native app container you've built` `key: 'AppName',` `},` `config: {` `// Disable or configure the auto-added vite-tsconfig-paths` `// https://www.npmjs.com/package/vite-tsconfig-paths` `tsConfigPaths: false` `// Disable auto-adding default tsconfig.json if not found` `ensureTSConfig: false` `},` `server: {` `// Configures production server to be compatible` `// defaults to node` `platform: 'node' | 'vercel'` `},` `react: {` `// enable the new react compiler` `compiler: true,` `// react-scan support, must add the react-scan package to your package.json` `// native is experimental, web is better supported` `scan: true | 'web' | 'native',` `},` `// The deps configuration is a powerful way to apply patches to node_modules` `// to configure patching and optimizeDeps for server-side.` `// we found the React Native and general node module package ecosystem to not always` `// play nice with universal apps, and this provides an easy way to configure them` `deps: {` `'moti/author': true,` `'@sentry/react-native': {` `version: '~15.2.0',` `'**/*.js': ['jsx']` `}` `},` `/**` `* Per-file control over how code transforms.` `* Defaults to SWC, runs babel before SWC if:` `*` ``* - options.react.compiler is `true`, on tsx files in your app`` ``* - `react-native-reanimated` is in your dependencies and a file contains a reanimated keyword`` `*` ``* Otherwise One defaults to using `@swc/core`.`` `*` `* Accepts a function:` `*` `* (props: {` `* id: string` `* code: string` `* development: boolean` `* environment: Environment` `* reactForRNVersion: '18' | '19'` `* }) =>` `* | true // default transforms` `* | false // no transforms` `* | 'babel' // force babel default transform` `* | 'swc' // force swc default transform` `*` `* // force babel, custom transform` `*` `* | {` `* transform: 'babel'` `* excludeDefaultPlugins?: boolean` `* } & babel.TransformOptions` `*` `* // force swc, custom transform` `*` `* | {` `* transform: 'swc'` `* } & SWCOptions` `*` ``* Babel defaults to preset `@babel/preset-typescript` with plugins:`` `*` `* - @babel/plugin-transform-destructuring` `* - @babel/plugin-transform-runtime` `* - @babel/plugin-transform-react-jsx` `* - @babel/plugin-transform-async-generator-functions` `* - @babel/plugin-transform-async-to-generator` `*` `*` `* SWC default to target es5 for native, es2020 for web.` `*` `*/` `transform: true` `})` `]` `} satisfies UserConfig` --- ## Page: https://onestack.dev/docs/environment ### Variables #### Loading .env files and process.env One handles environment variables for you in a few ways. First, it handles loading `.env` files during development and in production. In order to ease compatibility with ESM and CJS, One makes your loaded environment variables (from `.env` and from `process.env`) automatically be defined onto both `import.meta.env` and `process.env`. Second, One ensures that all environment variables loaded are only available to the server-side functionality, unless they are prefixed with either `VITE_` (to match Vite convention) or `ONE_PUBLIC_`. So, for example `ONE_PUBLIC_DAYTIME=false one dev` would set both `process.env.ONE_PUBLIC_DAYTIME` and `import.meta.env.ONE_PUBLIC_DAYTIME` to be the string "false" on both server and client. But, `SOME_RANDOM_VAR=2` would _not_ be exposed to the client side, because it lacks the appropriate prefix. ### Preset environment variables One also sets some environment variables for you, available on both `process.env` and `import.meta.env`. * `ONE_CACHE_KEY` is set to a random number for each production build, or once for each development server run. You can use this to have a stable cache key across releases. * `VITE_ENVIRONMENT` is set to `"client"` for client-side web, `"server"` for server-side web, and `"ios`" or `"android"` for native platforms, respectively. * `ONE_SERVER_URL` is set in development mode to the current running server URL, like `http://0.0.0.0:8081`. You are expected to set this yourself for production builds. * `ONE_DEFAULT_RENDER_MODE` is set to either `"ssr"`, `"ssg"`, or `"spa"` based on your defaultRenderMode setting * `ONE_APP_NAME` is set to your app.key setting * `TAMAGUI_REACT_19` is set to `1`, which turns on Tamagui React 19 mode automatically for you, for performance if using Tamagui. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/features System ### One is a complete solution to cross-platform development with React and React Native. We're experimenting with a features document to offer a high-level overview of One. See the Status document for the status of features. ## Vite The One Vite Plugin automates away much of the setup & boilerplate required for cross-platform React Native & web development. Out of the box, the One Vite plugin does the following: * Sets up a file system router in the `./app` folder * Generates types for routes and updates them when routes change * Enables loaders and tree shakes them out on client. Using the amazing babel-dead-code-elimination module * Enables the `deps` option that allows for easy node\_module patching * Swaps between React 18 for native and React 19 for web * Adds vite-tsconfig-paths plugin, unless it detects you've added it already * Loads `.env` file into `process.env` before running anything using dotenv * Adds a well-configured `tsconfig.json` if it doesn't exist * Configures native to resolve "react-native" export field in package.json * Configures platform-specific extensions `web|native|ios|android` for extensions `ts|tsx|js|mjs`, eg `.ios.ts` * Adds a ssr-css plugin that enables perfect dev-mode CSS on first load, matching production * Configures a wide variety of optimizeDeps presets for React Native library compatibility * Patches a large number of known-bad node\_modules to work with Vite * Ensures optimized deps across all routes are found before first load, avoiding hard reloads and slow loads in development mode ## Universal * Uses React Navigation routing and truly native layouts * Adds loaders that work on native and web * Adds a Link component for navigation * Adds a wide variety of hooks for routing * Adds `react-native-safe-area-context` and exports SafeAreaView ## Native * Implements a custom bundle output with React Native compatible commonjs * Implements custom hot reloading that works with commonjs * Implements a custom bridge to React Native for debug logging * Implements logic to support importing assets, like images ## Web * Adds `serve` command that sets up a Hono server for production * Implements SPA, SSG, and SSR style routing on a per-page and global basis * Server rendering - find JS and CSS for all nested routes and layouts and hoists it to header to avoid waterfalls and flickering * Automatically preloads next page JS when hovering a link or navigating to a new route, including loaders * Doesn't require up-front route map so no scaling cost * Automatically handles scroll restoration and allows configuration * Adds an easy `redirects` option * Adds `generateStaticParams` export for SSG and SSR routes * Implements API routes * Adds a page loading progress bar * Swaps `query-string` and `url-parse` libraries to save ~20Kb * Adds a Redirect component * Exports a variety of helpful utility functions * Analyze your web client-side bundle with `VXRN_ANALYZE_BUNDLE=1` * Guide for MDX support * Guide for light and dark mode Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/faq ### Why Vite? It feels light and simple, runs fast and is easy to customize. It tree shakes well, has tons of web features, and has best-in-class types, documentation, type-docs, RFCs and community. It also has a huge ecoystem and responsive team. ### Is this production ready? No. We think it's fun to work with already, and web is close to stable, but Native needs around half a year to get to stable. If you're planning on launching soon, we'd recommend using more proven technology. ### What's breaking? Testing, hot reloading, and general node module compatibility need work. See the Status documentation for more. ### Is this on Metro? Is it Expo Router? No, One started as a fork of Expo Router to test and improve vxrn. To test that we ported tamagui.dev to it and enjoyed it. To make that port work well we added render modes, loaders, middlewares, a lot of configuration to make a large amount of node\_modules work, a CLI, a production Hono server, and some other smaller features. ### Will you support RSC? In the best case scenario, we'd like to build our apps as though they were completely client-side, and have smart tools and libraries make them work fast. Mostly-client-side apps are great, and RSC adds a lot of new concept to learn. Further, once you have a great client-side data solution like Zero, you don't want to be going back to the server often, as it would degrate user experience. Finally, we do have server rendering and loaders that are both very simple, we save a lot of effort and complexity without server components. We'd like to explore bringing a simplified subset of RSC to One in the future. ### What's the npm package? It's `one`, thanks to Dan Maier. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing The `app` directory is where One handles universal file system routing. Every file you create in it will be turned into a route, except for files named `_layout` or `_middleware`. One only supports file system routes at this time, but we have a discussion for those who want to help us figure out a configuration-based approach. Here's an example: One routes support the popular feature of nested layouts, via `_layout.tsx` files, and parallel render-as-you-fetch data loading via `export loader`. ## Route Structure All `.tsx` files in the `app` dir are used to match your routes, besides layouts. Each individual file is referred to as a "page" to disambiguate them from the term "route," which we use to refer to the actual URL state in a browser on web (or React Navigation state on native). All pages must export a React component to be rendered. You can choose between two ways to export it: `export default` or plain `export`. If you don't `default` export, we look for the first `export` that is capitalized. The reason we support this is simple - the React Refresh library doesn't support hot reloading default exports. We're looking into modifying it to support them for route files, but until then you may want to simply use `export` to maintain a nice hot reloading experience. ### Simple Routes A simple page that matches the `/` route and will render on native and web: app/index.tsx If you are targeting web-only, you may use HTML elements: app/index.tsx All `index` files match against `/`, so `app/index.tsx` matches to your root `/` route. You can give your page a name as well, for example `app/about.tsx` will the `/about` route. ### Dynamic Routes The segments of a route, for example `/hello/world`, are referred to as "route parameters" or just "params", where "hello" and "word" are each a single parameter. Pages can match against parameters dynamically in two ways: matching a single parameter, or matching all parameters below them. #### Single Parameter Routes One uses brackets to match against a single dynamic parameter: `./blog/[slug].tsx` matches `/blog/post-one` and `/blog/post-two`: One will match `./blog/[slug].tsx` to any `/blog/*` route, and pass in the parameters to the route as follows: #### Rest Parameter Routes Much like JavaScript supports rest parameters, One supports rest parameter routes which are defined using the `[...rest].tsx` syntax. In the case where a user navigates to `/catalog/a/b/c`, the `[...rest].tsx` route would receive a params prop as follows: ### Not found routes To have a custom 404 page, you can create a `+not-found.tsx` route in any folder. It will act as the same as a `[...rest].tsx` route in that it will catch all sub-routes for that folder. Simply export a React component (default or named) from your not found page and it will render with a 404 status code. ## Routing Modes You can choose a rendering strategy on a per-page basis using a filename suffix. For more on these modes, see the next documentation page, Routing Modes. * `route+ssg.tsx` - Matches "/route", but will render the page as a SSG route. * `route+spa.tsx` - Matches "/route", but will render the page as a SPA route. * `route+ssr.tsx` - Matches "/route", but will render the page as a SSR route. * `route+api.tsx` - Matches "/route", but will render the page as an API route. ## Routing per-platform You can target a specific platform using the same specific extension convention as React Native - ie, using `.web.tsx`, `.native.tsx`, `.ios.tsx`, or `.android.tsx` as the last part of the filename. This lets you diverge the routing based on the platform. For example: ## Groups You can define a group by naming a folder with surrounding parenthesis: Groups are useful for a couple reasons: * They're useful to organize similar things without forcing you to nest URL segments. * They let you nest layouts, also without nesting segments. ## Types One generates types for your routes, so that you get type checking on a variety of things like the Link component. The types are generated into your root directory, in a `routes.d.ts` file. You must make sure your tsconfig.json picks up this file. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing-modes A unique feature of One is the ability to seamlessly route across a variety of route modes. What this means in plain english, is that you can choose whether each individual page within your routes uses one of a few strategies, such as rendering statically at build time, or rendering on-demand at request time. One supports the following routing modes, which are explained in detail below: `api`, `spa`, `ssr`, and `ssg`. You can set your global routing mode with the One vite plugin. The default is `ssg`. vite.config.ts To specify the routing mode on a per-page basis, you add a suffix to the filename, like so: * `route+ssg.tsx` - Matches "/route", but will render the page as a SSG route. * `route+spa.tsx` - Matches "/route", but will render the page as a SPA route. * `route+ssr.tsx` - Matches "/route", but will render the page as a SSR route. * `route+api.tsx` - Matches "/route", but will render the page as an API route. ## Modes Note that render modes mostly only apply to the web. On native, everything is essentially a SPA, with all the JS for every route included in the app the user downloads from the app store. Because native apps don't have to deal with the network, this SPA mode is ideal and has very little downsides. ### SSG (the default) When you don't add a `+` suffix to your route, it will default to an "SSG" route, unless you set the `web.defaultRenderMode` configuration otherwise. An example of a SSG route would be something like `app/blog.tsx` if you haven't changed the defaultRenderMode, or if you have `app/blog+ssg.tsx`. SSG Stands for "Server Side Generated", it's a strategy for web where you pre-render the entire HTML and CSS of a page at build-time fully on the server (or in CI) so that you can serve the page quickly from something like a CDN, without running any JavaScript at the time of request. We chose SSG as the default behavior as it serves as a good balance between simplicity and performance. What it is less good at is serving dynamic content to an individual user. That said - you can still do dynamic content on top of an SSG page. It's just that you'll have to have the dynamic content load in after the browser finishes downloading and parsing the JS for that page, and it will need to gracefully replace the static content that the user sees. This pattern is great for things like a SaaS homepage that shows mostly static content, but where you may want to load a logged-in menu for the current user. It's not good for something like a logged-in dashboard. ### SPA If you name a route with the `+spa` suffix, like `app/dashboard+spa.tsx`, it will no longer render on the server - either at build-time or at request. Instead, at build time One will build just the client-side JavaScript necessary. This render mode is great for highly dynamic use cases. Think a Linear, or a Figma, a metrics dashboard, or a user account panel. It is simpler to build, and doesn't require making sure every dependency works on both Node.js and in the browser. It's downsides include a slower initial load time, and worse SEO. Though we are working on allowing `_layout.tsx` files to also leverage render modes, in which case you could have a really nice hybrid mode where you render layouts statically or on the server, but keep the "meat" of the page dynamic. This would retain some upsides of SSR/SSG, while simplifying things quite a bit. ### SSR If you name a route with the `+ssr` suffix, like `app/issues+ssr.tsx`, you will get a server rendered page. This mode will generate the JS for both server and client at build-time, but instead of rendering out the HTML and CSS right then, it will instead wait for a request to the production server before it imports the server JS, renders the page, and then returns the HTML and CSS. This mode is great for things that need to be dynamically rendered more often than whenever you deploy. One example is something more like GitHub Issues. By server rendering a page like this, you get faster initial loads, and better SEO than a SPA-type page. But it is the most expensive in terms of cost - each request will now run a full render of the route, and also slower than SSG in terms of initial response time, at least by default. In the case of a GitHub Issues type page, what you'd do is cache the SSR response on a CDN, and then clear the CDN cache whenever data for that page updates. This is complex, and generally SSR is the most complex of the three render modes to support. Because it is more complex and more expensive, we generally recommend using SPA or SSG unless you are certain you can afford to pay for the extra render cost, and/or cache responses without too much trouble. ### API Adding `+api.tsx` to the end of your filename turns a route into an API route. This is a special type of route that is different from the other types in that it expect to be used as an API endpoint that you call from other routes or services, and not as a place an end-user would navigate to directly via their browser. API routes have a few standard exports. The default export acts as a catch-all: We've aligned API routes with the Web standard Request and Response objects. This ensures they will be easy to use, well documented, and have broad compatibility with web servers. Note that TypeScript and Node >= 20 ships by default with global Request and Response objects, so you don't need to import them at all. If you define a `./app/test+api.tsx` page with the above code, you can `curl https://localhost:3000/test` and you will receive a response with `Content-Type` set to `application/json`, and the contents of `{"hello":"world"}`. You may also export a function for each of the supported HTTP types: One exports a type helper called `Handler` that you can use to make typing easier: Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/one-serve System Serve your web apps for production with `one serve`, after you've successfully run `one build`. It takes the following arguments: Terminal One comes with a serve command that is powered by Hono. If you are only using SPA and SSG routes and aren't using loaders, you don't need to use `one serve` if you don't want to - you can statically serve the results of `dist/client` using any server you'd like. You must run a `one build` before serve. You can also run your production server programatically: Which takes options: Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing-loader Loaders are useful for one-time loading of data from the server to the client and can only be used in routes in your `app` directory. Loaders run on the server, for now they run based on their render mode - during build-time for SSG routes, or on each request for SPA or SSR routes. Loaders and their imports are removed from client bundles, so you can access private information from within the loader. The data returned from the loader will be passed to the client, and so should be clear of private information. To access the loader data on the client, you must use the `useLoader` hook: The useLoader hook is automatically type safe. ## Loader arguments Loaders receive a single argument object: ### params The `params` key will provide values from any dynamic route segments: app/user/\[id\].tsx ### path The `path` key is the fully resolved pathname: app/user/\[id\].tsx ### request Only for `ssr` type routes. This will pass the same Web standard Request object as API routes. ## Accepted Return Types Most JavaScript values, including primitives and objects, will be converted to JSON. You may also return a `Response`: ## Throwing a Response A final handy pattern for loaders is throwing a response to end it early: You can combine this with the redirect utility function: Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-Link `import { TextProps } from 'react-native'` `export interface LinkProps extends TextProps {` `children?: React.ReactNode` `/** Path to navigate to. */` `href: Href` `/** Forward props to child component. Useful for custom buttons. */` `asChild?: boolean` `/** Replace the current route without adding to the history. */` `replace?: boolean` `/** Should push the current route */` `push?: boolean` ``/** On web, this sets the HTML `class` directly. On native, no-op or can be used via a compile-time plugin. */`` `className?: string` `onPress?: (e:` `React.MouseEvent<HTMLAnchorElement, MouseEvent>` `| GestureResponderEvent` `) => void` `/**` ``* **Web only:** Specifies where to open the `href`.`` `*` `* - **_self**: the current tab.` `* - **_blank**: opens in a new tab or window.` `* - **_parent**: opens in the parent browsing context. If no parent, defaults to **_self**.` `* - **_top**: opens in the highest browsing context ancestor. If no ancestors, defaults to **_self**.` `*` ``* This property is passed to the underlying anchor (`<a>`) tag.`` `*` `* @default '_self'` `*` `* @example` `* <Link href="https://tamagui.dev" target="_blank">Open in new tab</Link>` `*/` `target?: '_self' | '_blank' | '_parent' | '_top' | string` `/**` ``* **Web only:** Specifies the relationship between the `href` and the current route.`` `*` `* Common values:` ``* - **nofollow**: Indicates to search engines that they should not follow the `href`. This is often used for user-generated content or links that should not influence search engine rankings.`` ``* - **noopener**: Suggests that the `href` should not have access to the opening window's `window.opener` object, which is a security measure to prevent potentially harmful behavior in cases of links that open new tabs or windows.`` ``* - **noreferrer**: Requests that the browser not send the `Referer` HTTP header when navigating to the `href`. This can enhance user privacy.`` `*` ``* The `rel` property is primarily used for informational and instructive purposes, helping browsers and web`` `* crawlers make better decisions about how to handle and interpret the links on a web page. It is important` ``* to use appropriate `rel` values to ensure that links behave as intended and adhere to best practices for web`` `* development and SEO (Search Engine Optimization).` `*` ``* This property is passed to the underlying anchor (`<a>`) tag.`` `*` `* @example` `* <Link href="https://tamagui.dev" rel="nofollow">Open</Link>` `*/` `rel?: string` `/**` ``* **Web only:** Specifies that the `href` should be downloaded when the user clicks on the link,`` `* instead of navigating to it. It is typically used for links that point to files that the user should download,` `* such as PDFs, images, documents, etc.` `*` ``* The value of the `download` property, which represents the filename for the downloaded file.`` ``* This property is passed to the underlying anchor (`<a>`) tag.`` `*` `* @example` `* <Link href="/image.jpg" download="my-image.jpg">Download image</Link>` `*/` `download?: string` `}` --- ## Page: https://onestack.dev/docs/components-SafeAreaView Exports SafeAreaView from react-native-safe-area-context. This enables easy "safe area" targeting for both web and native. One uses the SafeAreaProvider from React Navigation to set this up. We're working on improving this so you can control whether it is included and the initial window metrics on server. PreviousScrollBehavior Next useActiveParams Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-ScrollBehavior One automatically handles resetting scrolling to window.location.hash and changing scroll position to the top when you navigate to a new page. It also remembers your scroll position on each page you've visited and restores that position when you navigate back or forward to it. This mimics the default behavior that browsers do with multi-page apps. You can control this behavior by importing the `ScrollBehavior` component once in your app (we'd recommend doing so in your root `_layout.tsx`) and returning it in your component's virtual DOM. ScrollBehavior accepts a `disabled` prop which takes values of type `boolean` or string `"restore"`. If set to "restore," the scroll position will only reset to the top on new page navigation, but won't be restored to the previous scroll position when navigating back/forward. In general, you shouldn't need to use this component, but it may be helpful in specialized use cases where you need more control over the scroll restoration logic. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing-exports#generatestaticparams System This page catalogs all the special exports One supports from route files. Note that `_layout` routes are special and do not support these exports unless specified. ### generateStaticParams This export is needed for "ssg" type pages that use dynamic path segments (for example, `[id].tsx`). At build-time, One will expand the id segment into multiple pages, using the values you return from `generateStaticParams`. The entire function and export `generateStaticParams` will be removed at build-time and everything it depends on will be tree-shaken out of your client-side bundle. You'll need to return an array of objects, where the object keys match the name inside the brackets. As an example, we'll use nested dynamic segments: app/blog/\[month\]/\[year\]/\[slug\]+ssg.tsx If this function returned this: Then you'd generate one page at `/blog/10/2025/some-slug`. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-LoadProgressBar `export type LoadProgressBarProps = {` `/** How long after a navigation to wait before showing the progress bar (in ms) */` `startDelay?: number` `/** How long after a navigation completes to wait before hiding the progress bar (in ms) */` `finishDelay?: number` `/** The starting percent it should show the loading state at */` `initialPercent?: number` `/** How often the progress bar should update (in ms) */` `updateInterval?: number` `/** How often to skip an update (checked each during the loop) */` `sporadicness?: number` `/** Pass style to the inner View */` `style?: ViewProps['style']` `/** Pass onLayout to the inner View */` `onLayout?: ViewProps['onLayout']` `/** Pass children to the inner View */` `children?: ViewProps['children']` `}` --- ## Page: https://onestack.dev/docs/components-Redirect Redirect to another page when this page is focused. This component uses `useFocusEffect` to only run when the route it's rendered inside of is active. Once active, it will redirect immediately to a new route. ### Props Terminal `interface RedirectProps { href: string }` PreviousLink Next LoadProgressBar Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/helpers-utility-functions All functions listed below are exported directly by `one`. ### redirect A function that takes two arguments: a string `path`, and a number `status`. It accepts relative paths by using the `getURL` helper internally. On the server (including in loaders) it returns a `Response.redirect` object. On the client it calls `router.navigate` and returns void. ### getURL A function that takes no arguments and returns a string of the current URL of the running application on client or server. For example, in dev-mode by default this would be a string of `http://127.0.0.1:8081`. In non-development environments you will need to set `process.env.ONE_SERVER_URL` to your production URL with no trailing `/`. ### isResponse One uses `Request`/`Response` type objects for API routes, but for some environments doing an `instanceof Response` can fail, `isResponse` takes any value and returns true if it is a Response-like object. Useful for API utility functions. ### href A simple function allows for creating typed route strings. It's a type-level only check, at runtime it only validates that it is a string. ### setServerData, getServerData Developing For improving performance of client hydration on the web, you can pass data from the server to the client. Data must be serializable to JSON. Here's an example of a simple `useFetch`: app/index.tsx ### setResponseHeaders Developing A convenience function for setting headers of the current response from anywhere on the server. app/\_middleware.ts Where `headers` is of type Headers. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-mdx ### Setting up MDX for web In building out this beautiful website, and the equally beautiful tamagui.dev, the surprising most difficult part was getting MDX to work well. Since many blogs, apps, and documentation sites want to display Markdown content, we figured a guide showing how we did it will be useful. We think long-term someone should write a much better mdx package, as ours is simply pieced together from a bunch of packages and code we ported long ago to Next.js, but nonetheless it works. It uses a hodge-podge of Rehype plugins to do the trick, alongside mdx-bundler. ### Setting up your data dir The first step is to create a directory for your mdx files to live: Throw some stuff in that file: Terminal ### Setting up `@vxrn/mdx` Next, we add our mdx dependencies: `npm install @vxrn/mdx mdx-bundler` We set up our MDX components (using Tamagui in this example): features/MDXComponents.tsx And then all we need is a new route: app/docs/\[slug\].tsx Note that we `await import` the `@vxrn/mdx` library - this is because One by default builds out your server and API routes to CommonJS, but some of the rehype/remak dependencies are ESM. Long story short - this makes it work. ### Configuring Vite for MDX Support To ensure that MDX works correctly with your One project, you'll need to configure Vite. Update your `vite.config.ts` file with the following content: vite.config.ts This configuration does a few important things: 1. It sets `noExternal: true` in the SSR options, which tells Vite to bundle all dependencies for server-side rendering. 2. It explicitly marks `@vxrn/mdx` as an external dependency, which is necessary because it contains ESM modules that shouldn't be bundled. wwwwwwwwwwwwwwwwwww That _should_ do it. We say should, because there's a lot going on here, and many sub-deps inside `@vxrn/mdx` that need to work together. Let us know if this works for you! Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-dark-mode `import { Moon, Sun, SunMoon } from '@tamagui/lucide-icons'` `import { useSchemeSetting } from '@vxrn/color-scheme'` `import { Appearance } from 'react-native'` `import { View, isWeb, Paragraph, YStack } from 'tamagui'` `const schemeSettings = ['light', 'dark', 'system'] as const` `export function ThemeToggleButton() {` `const { onPress, Icon, setting } = useToggleTheme()` `return (` `<View group ai="center" containerType="normal" gap="$1">` `<View p="$3" br="$10" hoverStyle={{ bg: '$color2', }} pressStyle={{ bg: '$color1', }} pointerEvents="auto" cur="pointer" onPress={onPress} >` `<Icon size={20} />` `</View>` `<YStack>` `<Paragraph animation="100ms" size="$1" mb={-20} color="$color10" o={0} $group-hover={{ o: 1, }} >` `{setting[0].toUpperCase()}` `{setting.slice(1)}` `</Paragraph>` `</YStack>` `</View>` `)` `}` `export function useToggleTheme() {` `const [{ setting, scheme }, setSchemeSetting] = useSchemeSetting()` `const Icon = setting === 'system' ? SunMoon : setting === 'dark' ? Moon : Sun` `return {` `setting,` `scheme,` `Icon,` `onPress: () => {` `const next = schemeSettings[(schemeSettings.indexOf(setting) + 1) % 3]` `if (!isWeb) {` `Appearance.setColorScheme(next === 'system' ? scheme : next)` `}` `setSchemeSetting(next)` `},` `}` `}` --- ## Page: https://onestack.dev/docs/routing-middlewares Developing Place a `_middleware.ts` file anywhere inside your `app` directory to create a middleware. Here's an example: Middlewares can be nested and will run from top to bottom on matching routes. app/\_middleware.ts The `createMiddleware` function is a simple helper for types. Middlewares receive one argument, an object with three properties: * `request` is a Request for the current request. * `next` is a function that returns a promise. * `context` is an Object which you can mutate to pass data between middlewares. The `next` function will run the rest of the middlewares and then the current route, and will return the final Response. This lets you run some logic after the full route has been processed, and before it returns the final response. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/one-build Building your app for production happens through `one build`. It takes the first argument as the platform, which defaults to `web`. `npx one build [web | ios | android]` To build for iOS for example, you would run: `npx one build ios` ## Building for web When building One for web, the output will be a mostly static directory of HTML, CSS, and JavaScript. Internally, One runs a few different Vite production builds to output client, server, and API folders. This output is always in the `dist` directory. Ensure your `.env` file has a `ONE_SERVER_URL` set to your production URL, like: Terminal We provide a production server based on Hono with `one serve` that handles loaders, SSR, and API routes for you. You can choose a deploy target for that server, for now we support `node` and `vercel` (in beta). This can be chosen via your Vite configuration: vite.config.ts The CLI will print instructions on how to deploy to the respective platform you choose after you run `npx one build`. If you are only using SPA and SSG routes and aren't using loaders, you can statically serve the results of `dist/client` using any server you'd like. ## Building for native We don't have an end-to-end build command yet, but One is designed to be easy to integrate into existing Expo or React Native build processes. If you're familiar with EAS, see how to Build and Deliver Native Apps with EAS. Or check out the iOS Native Guide for instructions on building your iOS app with Xcode. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing-layouts Layout files can be placed in any folder inside the `./app` directory. They serve as the frame for the routes contained within that folder, and they can nest inside of each other if you place them in sub-directories. Layouts must render one of the following to show the matched pages that exist in their directory: * Slot: Directly show sub-route with no frame. * Stack: Creates a React Navigation Stack of sub-routes. * Tabs: Creates a React Navigation BottomTabs with sub-routes. * Drawer: Creates a React Navigation Drawer with sub-routes. This looks something like this at the simplest: app/\_layout.tsx As of now, layouts don't support loaders. If you need to access the current route params, you can use the useParams hook. ## Root Layout The root layout on web controls the entire page, from `<html>` on down. You can optionally return `html`, `body`, and `head` elements in your `app/_layout.tsx` and they will "just work" - on web, it will be output, and on native it won't so as to avoid rendering errors: app/\_layout.tsx ### useServerHeadInsertion The root `_layout.tsx` can use a special hook that allows it to insert tags into your `<head />` tag after server rendering finishes. This is an edge-case that is mostly only useful for CSS-in-JS libraries. You can use it like so: ## Groups and layouts If you'd like to logically group together some pages without creating a sub-route, you can use a group folder by naming it like so: Groups let you add extra layouts. If done right, this gives you a lot of control over how your app feels. ## Nested layouts example A common pattern that apps have is something like Twitter/X, where you have bottom tabs for your "top level" views, but then on some of the bottom tab sections, you want to have a Stack that remembers its state inside just that tab. This pattern can be incredibly verbose to link together with React Navigation, and takes a bit of tinkering to figure out with One. Since it is common and a useful example, lets walk through how you'd build on using One's file system routing. Here's our file structure: The top level Layout will define our tabs: app/\_layout.tsx This will set us up with three bottom tabs: Feed, Notifications, and Profile. The Notifications and Profile tabs for now will just show their content directly, but inside of the Feed tab, we want to show a stack. We set up the stack in `(feed)/_layout.tsx`: (feed)/\_layout.tsx One thing we're showing here is that the layout is diverging between web and native. On web, we are showing a Slot, while on Native we show a Stack. This is because browsers feel better without stacks - the native back/forward button serves as our stack controller. On Native we are defining the configuration for each sub-screen with the `Stack.Screen` component. The Stack component is a React Navigation native stack navigator and nothing more, it accepts all the props you'd expect. You'll notice we are matching the `name` to the file names names of the sub-routes, without the `.tsx` extension. This will get you a nice Stack-inside-Tabs pattern that is common on native apps, all with just two layouts and a few routes. ## Limitations As of today layouts don't support loaders. This is on our radar, please chime in if this is helpful for you. Also note that `useParams` won't work in layouts, as they are never nested inside a route. Instead, you can use `useActiveParams`. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/exports-withLayoutContext ### Create your own layouts. Today there are a few built-in navigators: Slot, Stack, etc. But you may want to create your own. A good example of this is the new `react-native-bottom-tabs` library, that exports a React Navigation compatible navigator. To make any React Navigation navigator work inside a One layout, use `withLayoutContext`: `import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation'` `import { withLayoutContext } from 'one'` `const NativeTabsNavigator = createNativeBottomTabNavigator().Navigator` `export const NativeTabs = withLayoutContext(` `NativeTabsNavigator` `)` Now you can use `NativeTabs` in any `_layout.tsx` file. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useActiveParams This will return the segments of the current URL paths, including dynamic path segments. Note that this function, as opposed to useParams, will update even when the path is not relevant to the route it is used inside. In general when you need to access params , use `useParams`, as these will only update when the route is focused. This hook is useful for things like analytics where you need to access every change globally. PreviousSafeAreaView Next useFocusEffect Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/routing-navigation When you want to move between a page, you have a few options. Examples on this page will reference this routing structure: ## Link You can use the Link component as follows: app/index.tsx Link accepts the following props, where `Href` is your typed file system routes (or a string if you turn off typed file system routing) and `TextProps` is the React Native Text element props: ## useLinkTo This hook will generate both an `href` string and an `onPress` function, see the useLinkTo docs for how to use it. ## useRouter This hook returns a static object used for routing. It will never update, and is only used for imperatively controlling the router. Useful for programmatically controlling the routing, and choosing between a `push` or `replace`. See the useRouter documentation page for more. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useRouter `type Router = {` `/** Go back in the history. */` `back: () => void` ``/** If there's history that supports invoking the `back` function. */`` `canGoBack: () => boolean` `/** Navigate to the provided href using a push operation if possible. */` `push: (href: Href, options?: LinkToOptions) => void` `/** Navigate to the provided href. */` `navigate: (href: Href, options?: LinkToOptions) => void` `/** Navigate to route without appending to the history. */` `replace: (href: Href, options?: LinkToOptions) => void` `/** Navigate to the provided href using a push operation if possible. */` `dismiss: (count?: number) => void` `/** Navigate to first screen within the lowest stack. */` `dismissAll: () => void` ``/** If there's history that supports invoking the `dismiss` and `dismissAll` function. */`` `canDismiss: () => boolean` `/** Update the current route query params. */` `setParams: <T = ''>(` `params?: T extends '' ? Record<string, string | undefined | null> : InputRouteParams<T>` `) => void` `/** Subscribe to state updates from the router */` `subscribe: (listener: RootStateListener) => () => void` `/** Subscribe to loading state updates */` `onLoadState: (listener: LoadingStateListener) => () => void` `}` --- ## Page: https://onestack.dev/docs/common-issues This document collects tips and tricks for debugging common issues. If you'd like to see a workaround documented here, please submit it to our GitHub issues. ### General errors when server rendering If you see an error while rendering from the server, it can often be hard to diagnose. There's a few ways to debug it. First, we've found turning off Vite external dependencies entirely will often help, what it does is make Vite use esbuild and apply CommonJS transforms for compatibility, transforming all of them into your `.vite` directory. You can enable it like so: vite.config.ts The reason we don't turn this on by default it is can cause other issues, namely if a node module relies on, say, \_\_dirname, or \_\_filename, then when they are transformed into the `.vite` folder these paths will change. Another reason we don't turn it on by default is that it can slow things down. If you do find the individual module that is causing an issue, you can also configure it quickly using the `deps` option in the One Vite plugin configuration. ### Uncaught SyntaxError: The requested module ... does not provide an export named 'default' In general you want to add any dependency like this to `optimizeDeps.exclude`. But One has some default optimizeDeps options we add that may prevent that. You can use `fixDependencies` in your `vite.config.ts`: ### `require` is not defined The first thing you'll want to try is optimizing this dependency using the Vite interop transforms: vite.config.ts You can also try using one of the commonjs plugins, either `vite-require` or `vite-plugin-commonjs`. ### Common React Native package issues When using bundlers like Vite, Rollup, or esbuild, developers often encounter issues with React Native and Expo packages originally designed to work only for Metro. Metro’s tolerant nature allows certain practices that other bundlers may not support, such as: * Including Flow types in distributed `.js` files. * Including JSX syntax in `.js` files instead of `.jsx`. * Releasing packages as ESM-only without proper declarations in `package.json` (e.g., missing `"type": "module"`) or appropriate `.mjs` extensions. * Omitting import path extensions in distributed ESM, which can prevent Node.js from running those modules correctly, and break SSR. These issues can break builds and require specific handling to make the packages compatible with non-Metro bundlers. Related error messages you might encounter includes: * `The JSX syntax extension is not currently enabled.` * `The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able to parse JSX syntax.` * `SWC failed to transform file` following with a syntax error parsing something like `import type`. To workaround these issues, you can use the package patching feature of One to fix the packages that are causing problems. Here's an example: vite.config.ts For more details, see the `deps` section in Configuration. We encourage you to open an issue on our GitHub repository if you encounter similar issues, as your feedback helps us improve One's compatibility with React Native packages.If you've resolved an issue with a patch, we'd love for you to share your patch configuration. Alternatively, feel free to reach out, and we'll gladly assist in finding a solution to get things working. If appropriate, it's also nice to reach out to the maintainers of those packages to help improve their compatibility with non-Metro bundlers. ### Application has not been registered One calls AppRegistry.registerComponent for you to set up your React Native entry component. If you React Native app is erroring on startup, you need to make sure your One Vite plugin is configured to register the component: vite.config.ts Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-tamagui ### Setting up Tamagui on One Tamagui and One go together so well you'd swear they were made by the same person. When targeting both native and web, and wanting to share UI, you don't have many options. Tamagui does many things that no other universal libraries do: all of it's features work with SSR without flashing of styles, it supports spring and CSS animations (also SSR safe), it supports light and dark mode (also SSR safe), it's fully type safe, it supports container queries, and it outputs nearly ideal code for each platform. Try concrete Tamagui examples by running `npx one`. We're going to support SSR-safe light and dark mode with `@vxrn/color-scheme`: `npm install tamagui @tamagui/config @tamagui/vite-plugin @vxrn/color-scheme` Be sure to check out the Dark mode guide for more detail there. Next, update your root `_layout.tsx`: app/\_layout.tsx And add your `config/tamagui.config.ts`: config/tamagui.config.ts The reason we separate this file out is that it lets the Tamagui compiler optimize things. You don't have to do this, but it's pretty easy. This preset config we recommend as it lets you just get working on your app, but if you want a custom configuration, the `npx one` starter has a nice custom configuration example. Next for the Vite plugin. You don't need to set up the Tamagui Vite plugin, but even if not using the optimizing compiler, it adds a lot of nice stuff: debug attributes, broader react-native compatibility, and the optimizing compiler. vite.config.ts ### Optimizing further If you want to optimize things a bit further, we can output the Tamagui design system CSS to a stylesheet by changing our plugin configuration: vite.config.ts And then changing our root layout to add `disableInjectCSS`, and instead importing the CSS at the top: app/\_layout.tsx That should be about it! You now have SSR safe styling, light and dark mode, and a bunch of advanced styling features. Check out the Tamagui site for a lot documentation. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-Slot This component should only be rendered inside a `_layout.tsx` file, where it will serve as the location that children will render for routes below the layout. This is the simplest option for rendering children, it does not create a Stack, Drawer, or Tab view, instead it just renders the children directly. `import { Slot } from 'one'` `export default function Layout() {` `return (` `<Slot />` `)` `}` PreviousMiddlewares Next Stack Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-eas Expo Application Services (EAS) is a service that helps you build and deploy your React Native apps without the hassle of managing code signing, provisioning profiles, and other complexities of native development by yourself. Native apps built with One can be deployed using EAS. If you created your One app using one of our templates, it would already work with EAS out of the box. If not, refer to the "Setup Your One App for EAS" section below. After the initial EAS setup (`eas build:configure`), you will be able to run `eas build` to build your app, and `eas submit` to submit it to the App Store or Google Play directly in your One project. For more instructions, see the "Create your first build" guide in the EAS documentation. It's possible that you'll need to configure your app for distribution, such as setting an unused bundle identifier in `app.json` for your app. Generally, following the instructions you saw in the terminal and the EAS documentation should be sufficient to resolve most issues. A common type of error you'll probably see in an EAS build is: Terminal This is because EAS defaults to a relatively old version of Node.js. To fix this, you can modify your `eas.json` and specify the version of Node.js like this: ## Setup Your One App for EAS While our templates are configured in a way that works with EAS, if you're starting from scratch or migrating an existing project, some setup will be required to make your One app deployable with EAS. 1. Ensure `expo` is installed in your project. 2. Add a `react-native.config.cjs` to your project root (next to your `package.json`) with the following content: 3. Modify your `app.json` (or `app.config.js`) and add `vxrn/expo-plugin` to `expo.plugins`. This plugin will hook into the Expo build process running on EAS to ensure your One app is built correctly: Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-ios-native ### Run your iOS app in development with custom dependencies, or build it for App Store and TestFlight This guide explains how to build and upload your iOS app to the App Store or TestFlight, or run it locally during development with custom native dependencies. You'll use the `prebuild` command to generate the native code and Xcode project for iOS, open it with Xcode, and either run the app or archive it for distribution. **Note**: If you just want to run your iOS app in development mode without any custom native dependencies, you don't need this guide. All you need is an iOS device with Expo Go installed. It should auto-detect from your simulator, or press q, r in your terminal which is running the One dev server and follow the instructions to open the app in Expo Go. **Prerequisites**: To build and run the iOS app, you'll need a Mac with Xcode installed. To upload the app to the App Store or TestFlight, you'll additionally need a paid Apple Developer account. If you are starting with a bare React Native or Expo project without using one of our templates, please follow the instructions in the Project Setup section to configure your project first. ## Prebuild Before you can run or build your iOS app, you'll need to generate the native code and Xcode project for iOS. First, ensure the `prebuild:native` script is defined in your `package.json`: Next, run the following command in your terminal: `npm run prebuild:native` This command may prompt you with some questions and could take a few minutes to install CocoaPods dependencies on it's first run. Once finished, your iOS project will be available in the `./ios` directory and ready for use. ## Running the App in Development To run the app, open the `.xcworkspace` file in the `./ios` directory. The filename will vary based on your app's name, but you can generally open it by running this command: Terminal Once Xcode opens, ➊ choose a simulator or physical device, and ➋ click the run button to build and launch the app.  ## Building for Production Open the .xcworkspace file from the ios directory. You can do this by running the following command: Terminal Once Xcode opens, follow these steps: * ➊ Click your app project in the Project Navigator. * ➋ Under “TARGETS,” select your app. * ➌ Go to the “Signing & Capabilities” tab. * ➍ Ensure “Automatically manage signing” is checked. * ➎ Select a development team.  You need a paid Apple Developer Program account to upload apps to the App Store or TestFlight. If you're not signed into your Apple Developer account in Xcode, select "Add an Account…" from the dropdown to log in. If you encounter any other problems, try to follow the instructions that Xcode give you to fix them. Once code signing is set up, click "Product" → "Archive" on the menu bar to start the Archive process.  After the archive process completes, Xcode will automatically open the Organizer window with your build. From there, click "Distribute App" to upload the app to the App Store or TestFlight.  You can also open the Organizer window by clicking "Window" → "Organizer" on the menu bar. ## Project Setup We've set this up in most project templates, so you can skip this section if you've created your project with `npx one`. ### React Native You should add a `react-native.config.cjs` file in your project root to configure React Native to use Vite as the JS bundler during the native build process: By doing so, the `react-native build` command will be overwritten to use Vite as the JS bundler instead of Metro. If you are using React Native 0.74.x or below, we rely on a patch to the `@react-native-community/cli` package to make this work. For the patch to apply, you'll need to start the dev server at least once. ### Expo Prebuild If you're using Expo Prebuild, you should also add `"vxrn/expo-plugin"` in your app config (app.json/app.config.js/app.config.ts): ### iOS Project Setup This isn't necessary if you're using Expo Prebuild, as `vxrn/expo-plugin` already takes care of this. In the "Bundle React Native code and images" phase, make the following change in the shell script: Terminal  ## Troubleshooting ### `ios/*.xcworkspace` isn't there Try `cd ios && pod install && cd ..`. ### Error: `node: No such file or directory` during Xcode build This often comes with `Node found at: /private/var/folders/.../T/xfs-.../node` and then `/private/var/folders/.../T/xfs-.../node: No such file or directory`. Try to delete `ios/.xcode.env.local`. ### Error: `No Metro config found` 1. Make sure there's a `react-native.config.cjs` in your project which contains something like this: 2. Make sure you have `vxrn/expo-plugin` included in your `app.json` (or .js, etc.): 3. Try to delete the `ios` directory and run `prebuild` again. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/one-dev Developing an app with One is as simple as `one dev`. It takes the following arguments: Terminal The One development server serves both web and native apps on the same port at the same time, and should hot reload both at once. When you see the `Server running on` message with a host and port, that is the same for both native and web. ### Under the hood The development command does a few things: * Runs an underlying Vite development server * Sets up a parent server using vxrn for native The vxrn library is developed by the One team, and is what enables One to build and serve React Native apps with Vite. The way it works today is that it runs a server above Vite, which adds endpoints to support the various endpoints that React Native expects in development. We think we can eventually migrate this to be a single Vite server, but it was simpler for us to implement it this way - React Native expects to communicate over multiple websockets, and getting that set up properly was simpler with the two-server approach. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-Stack This component should only be rendered inside a `_layout.tsx` file, where it will serve as the location that children will render for routes below the layout. Stack is simply a React Navigation Native Stack view and accepts the same props as React Navigation. ### Stack.Screen You can customize the children of the Stack in your layout by passing a children prop to Stack has Stack.Screen elements, like so: The `name` must match the full name of the file inside `app`, without the extension but including groups. In this example we are setting `index`, `[id]`, and `sheet` screens, which would correspond to `index.tsx` and `[id].tsx` and `sheet.tsx` pages in the same directory. This is a convenient way to configure settings for each page up front, but you could also render `Stack.Screen` inside each individual page so you can access data loaded inside that page. The upside of doing it in the layout is that it will configure things before any stack animation runs on enter, with the downside being that you can't access page-level data. The `options` property passes to the React Navigation NativeStack, and so takes the same options. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-Tabs This component should only be rendered inside a `_layout.tsx` file, where it will serve as the location that children will render for routes below the layout. Tabs is simply a React Navigation Bottom Tabs view and accepts the same props as React Navigation. For now, you do need to set the `href` option on each Tabs.Screen to match the route file name. The plan is to automate this in the future. `import { Tabs } from 'one'` `export default function Layout() {` `return (` `<Tabs>` `<Tabs.Screen name="explore" options={{ title: 'Explore', href: '/explore', }} />` `</Tabs>` `)` `}` Adding a new tab route has a bug currently that can cause it to not show up until you re-run your app with `--clean`. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/components-Drawer Notice - Drawer is current disabled as we work through a final issue with react-native-gesture-handler. It should be landing very shortly. This component should only be rendered inside a `_layout.tsx` file, where it will serve as the location that children will render for routes below the layout. Drawer is simply a React Navigation Drawer view and accepts the same props as React Navigation. `import { Drawer } from 'one'` `export default function Layout() {` `return (` `<Drawer />` `)` `}` Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useParams Returns an object that describes the route segments. This will only update for the route it's used inside of, and avoids re-renders when, for example, a user navigates to another stack view that isn't matched. This is the recommended way to access the URL parameters. If you need to access parameters across all routes, see useActiveParams. PrevioususeNavigation Next usePathname Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useFocusEffect System wwwwwwwwwwwwwwwwwww Similar to React `useEffect`, but only runs when the current route is focused. This is directly based off React Navigation's useFocusEffect. You must pass it an array as the second argument to determine memoization, otherwise the effect will not change. Example: `import { useFocusEffect } from '@react-navigation/native'` `import { useCallback } from 'react'` `function Profile({ userId }) {` `const [user, setUser] = React.useState(null)` `useFocusEffect(` `() => {` `const unsubscribe = API.subscribe(userId, (user) => setUser(user))` `return () => unsubscribe()` `},` `[userId]` `)` `return <ProfileContent user={user} />` `}` Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useLinkTo Used to create props that can be passed to a React Native or web component for creating a custom link. Accepts a single argument of type: `type LinkToProps = {` `href: Href;` `replace?: boolean` `}` Note that `Href` is a string that is typed based on your file system routes. Returns an object of type: `type LinkToResult = {` `href: string` `role: 'link'` `onPress: (event: e?: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent) => void` `}` PrevioususeIsFocused Next useLoader Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-usePathname Returns a string that represents the current pathname. It takes no arguments and will update every time the route changes. PrevioususeParams Next useRouter Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/guides-migrating-create-react-app-cra-to-vite-with-one ### One makes a great replacement for Create React App. With a few simple steps, you can move over to One and enjoy the power of the Vite ecosystem. Migrating from Create React App (CRA) to One is easy. In this guide we'll use the default CRA output and show how you can move over to One. One is a React framework for web, iOS, and Android, built on the power of Vite - it's actually just a single Vite plugin. One saves you a lot of complexity when moving from CRA, it offers faster build times, built-in TypeScript support, SPA or server rendering, file system routes, and seamless cross-platform development capabilities. Let's walk through the migration process step by step. ### Step 1: Add One to Your Project First, let's add One and its dependencies to your project: `npm install one` ### Step 2: Update Your Project Structure One uses a file-based routing system, which means we need to adjust our project structure slightly. Here's what we'll do: ### Step 3: Configure Vite Create a new `vite.config.ts` file in your project root with the following content: vite.config.ts This minimal configuration sets up One with Vite, enabling all the goodness that comes with it. Note that we're setting `defaultRenderMode` to 'spa' (Single Page App) for compatibility with Create React App. This ensures that your app behaves similarly to how it did with CRA, rendering entirely on the client side. The 'spa' mode is the simplest strategy, where your app is not rendered on the server at all. Instead, only the JavaScript to render your page is served to the user's browser. This avoids some of the complexity associated with server-side rendering, making it a good starting point for migrating from CRA. If you want to explore other rendering modes later, such as Static Site Generation (SSG) or Server-Side Rendering (SSR), you can change this setting. Each mode has its own trade-offs in terms of performance, SEO, and complexity. For more information on the available render modes and their implications, refer to the One configuration documentation. ### Step 4: Update package.json Scripts Let's update our `package.json` scripts to use One's commands: package.json These new scripts replace the CRA equivalents, giving you access to One's optimized development and build processes. ### Step 5: Update Your Main Component Now, let's update our main component. We'll move `src/App.js` to `app/index.tsx` and make a few adjustments: app/index.tsx Notice how we've updated the import paths using the `~` alias. One supports `tsconfig` by default, and will also add a `tsconfig.json` file if you don't have one already, with the `~` path alias configured. Of course, you can change this to suit your needs. ### Step 6: Remove Unnecessary Files You can now safely remove the following files: * `public/index.html` * `src/index.js` * `src/reportWebVitals.js` * `src/setupTests.js` One handles app rendering and provides its own optimized setup for these functionalities. ### Step 7: Run Your Migrated App You're all set! Now you can start your newly migrated One app: `npm run start` Visit http://localhost:8081 to see your app in action. Use the `--port` flag if you need to change the port. ## Next Steps Congratulations! You've successfully migrated from Create React App to One. Here are some next steps to consider: 1. **Explore One's features**: Check out the One documentation to learn about its powerful features like SSR, file-based routing, and more. 2. **Optimize for production**: One provides excellent production optimizations out of the box. Run `yarn build` to create an optimized production build. 3. **Add Tamagui**: For a powerful, fully type-safe UI kit that works seamlessly with One, consider adding Tamagui. It provides SSR-safe styling, animations, and themes. 4. **Implement dark mode**: One makes it easy to add SSR-safe dark mode. Check out our Dark Mode guide for a quick setup. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useNavigation Return the navigation object for the current route. It takes an optional argument named `parent` of type `string`: * `parent` - Provide an absolute path like `/(root)` to the parent route or a relative path like `../../` to the parent route. It returns the result of `useNavigation` from React Navigation, you can see the methods available on this object on the React Navigation navigation prop reference page. You should prefer to use `useRouter` as it is strongly typed and only exposes functions that are guaranteed to work within One, but useNavigation gives you lower level access to the underling React Navigation instance. Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useIsFocused  Beta Beta X Github Discord System wwwwwwwwwwwwwwwwwww  Beta Introduction Installation Configuration Environment Features Status FAQ Re-export of useIsFocused from React Navigation. Returns a boolean, `true` if the current screen is active. PrevioususeFocusEffect Next useLinkTo Edit this page on GitHub. --- ## Page: https://onestack.dev/docs/hooks-useLoader `import { useLoader } from 'one'` `export function loader() {` `return { hello: 'world' }` `}` `export default function Page() {` `const data = useLoader(loader)` `return (` `<>{data.hello}</>` `)` `}`