One is a single Vite plugin exported from one/vite, most all configuration is done through the plugin in your vite.config.ts, which takes a single object with entirely optional properties:
one
vite.config.ts
importtype{ UserConfig }from'vite'
import{ one }from'one/vite'
exportdefault{
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,
},
],
},
// all native-specific config nests in app:
app:{
// 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
Choose between one of ssg, spa or ssr. The default is ssg.
Each mode has trade-offs:
ssg stands for "Static Site Generated", it is the default mode.
SSG is a great general purpose strategy, where your pages are rendered fully at build-time and turned into static HTML. This means you can get a "perfect" first load, where your app is fully rendered with all content, servable easily by a CDN.
The JS is sent alongside it, and React will hydrate your app and resume running from there. The downside is that you do have some complexity in needing to ensure your pages can render in node, the upside is increased performance and SEO on the web.
spa stands for "Single Page App", which is the simplest strategy.
SPA mode will not render the page on the server at all, and instead only serve the JavaScript to render your page
to the users browser. This mode avoids some complexity - you don't have to make your React code
ssr stands for "Sever Side Rendered".
This mode will render your page on the server at the time the browser requests it, running loaders as well for each request.
This mode is likely the least useful, as it means that you must make a full trip to your database at the time of each request, and your client is left waiting for the full render from the server before it can show anything. That said, this mode can be useful if you have dynamically generated content but still want to have the best possible SEO, at the cost of some complexity in supporting server rendering.
You can apply server-level redirects. One serves your app with Hono in production, and so the redirect pattern is simple: your source will be passed to Hono.app.get(source). Any matching path segments that start with : will be replaced into the matching segments in destination. And the redirect will send a response type of 301 if permanent is true, otherwise it will be 302.
deps
While building One we found ourselves needing to apply small patches to a variety of packages. Packages in the React Native ecosystem are often published only for Metro, and so have a variety of weird setups.
We built an internal Vite plugin to easily apply patches across any package manager, but after installing One into a few larger production projects, we found ourselves wanting to have access to it.
The deps option is a powerful way to coerce your node_modules to be compatible with Vite.
// passing an object lets you easily apply patches to your actual node_modules
'@sentry/react-native':{
// you can specify a semver range to only apply to certain versions:
version:'~15.2.0',
// you can use globs to apply to only specific files
// if you pass an array value, One will apply one of two transforms to matching files:
// swc => @swc/core will transform your file into CommonJS compatible code, removing JSX as well
// flow => the FlowType transformer will run to remove Flow types
'**/*.js':['flow','swc']
// you can also apply custom transforms using a function
'dist/js/utils/environment.js':(contents)=>{
if(typeof contents !=='string')return
return contents.replace(
`import { version as RNV } from 'react-native/Libraries/Core/ReactNativeVersion';`,
`import { Platform } from 'react-native';\nconst RNV = Platform.constants.reactNativeVersion;\n`
)
},
},
},
})
]
} satisfies UserConfig
Any transforms that are applied to specific files in node_modules will only be applied once. The original files will be stored alongside the transformed ones.
This makes our lives implementing React Native support as we can specify configuration for native environments separately. We do this by defining ios and android environments, respectively.
For web, we use the default client and ssr environments, for client-side and server-side, respectively.
What this means for you as a user is that you likely want to lean on using environment to configure as much as you can in your vite.config.ts. It may also mean that some plugins can cause issues by configuring things across all four environments when you really only want it for client-side, or not. We're working on some helpers to ease this.
Other Exports
The one/vite import has a few other exports that may be useful.
When you are setting an alias in Vite, it wants you to fully specify the import path. We use resolvePath to help with this, it's a bit like require.resolve that works in ESM or CJS. It's also similar to import.meta.resolve, except it returns an absolute path that Vite expects rather than a file path.