├── .eslintrc.js
├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ └── bug.yaml
├── .gitignore
├── .npmignore
├── .prettierrc.js
├── .vscode
└── settings.json
├── .yarn
└── releases
│ └── yarn-4.7.0.cjs
├── .yarnrc.yml
├── LICENSE
├── README.md
├── babel.config.js
├── create-solito-app
├── .yarn
│ └── install-state.gz
├── index.ts
├── package.json
├── readme.md
├── run.js
├── tsconfig.json
└── yarn.lock
├── develop
├── appdir
│ ├── .gitignore
│ ├── .yarn
│ │ ├── install-state.gz
│ │ └── releases
│ │ │ └── yarn-4.7.0.cjs
│ ├── .yarnrc.yml
│ ├── README.md
│ ├── app
│ │ ├── api
│ │ │ └── hello
│ │ │ │ └── route.ts
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ ├── page.js
│ │ ├── styles-provider.tsx
│ │ └── users
│ │ │ └── [userId]
│ │ │ └── page.tsx
│ ├── next.config.js
│ ├── package.json
│ ├── public
│ │ ├── next.svg
│ │ ├── thirteen.svg
│ │ └── vercel.svg
│ ├── tsconfig.json
│ └── yarn.lock
└── next
│ ├── next-env.d.ts
│ ├── next.config.js
│ ├── package.json
│ ├── pages
│ ├── _document.tsx
│ ├── cover.tsx
│ ├── import.tsx
│ └── index.tsx
│ ├── public
│ └── mountains.jpg
│ ├── tsconfig.json
│ ├── yarn-error.log
│ └── yarn.lock
├── docs
├── .gitignore
├── .yarn
│ ├── install-state.gz
│ └── releases
│ │ └── yarn-4.1.0.cjs
├── .yarnrc.yml
├── README.md
├── babel.config.js
├── docs
│ ├── app-directory
│ │ ├── hooks.md
│ │ └── overview.md
│ ├── community.mdx
│ ├── compatibility.md
│ ├── expo-router.mdx
│ ├── gradual-adoption.md
│ ├── guides
│ │ ├── auth.md
│ │ ├── dynamic-route.md
│ │ ├── expo-router.md
│ │ └── new-route.md
│ ├── index.mdx
│ ├── install.md
│ ├── intro.md
│ ├── methodology.md
│ ├── moti-pressable.mdx
│ ├── recipes
│ │ ├── deep-linking.mdx
│ │ ├── deploying.mdx
│ │ ├── icons.mdx
│ │ ├── modals.mdx
│ │ ├── platform-code.mdx
│ │ ├── redirects.mdx
│ │ ├── scroll-view.mdx
│ │ ├── tree-shaking.mdx
│ │ └── use-is-focused.mdx
│ ├── resources.mdx
│ ├── starter.md
│ ├── tailwind.mdx
│ ├── typescript
│ │ ├── navigation-options.mdx
│ │ └── next-replacements.mdx
│ ├── usage
│ │ ├── image.mdx
│ │ ├── link.mdx
│ │ ├── moti-link.mdx
│ │ ├── params.mdx
│ │ ├── text-link.mdx
│ │ ├── use-link.mdx
│ │ └── use-router.mdx
│ ├── v2.mdx
│ └── v4.mdx
├── docusaurus.config.js
├── package.json
├── sidebars.js
├── src
│ ├── components
│ │ ├── HomepageFeatures.js
│ │ └── HomepageFeatures.module.css
│ ├── css
│ │ └── custom.css
│ └── pages
│ │ └── index.module.css
├── static
│ ├── .nojekyll
│ ├── font
│ │ ├── Satoshi-Bold.ttf
│ │ ├── Satoshi-Italic.ttf
│ │ ├── Satoshi-Medium.ttf
│ │ ├── Satoshi-MediumItalic.ttf
│ │ └── Satoshi-Regular.ttf
│ └── img
│ │ ├── 4.png
│ │ ├── app.PNG
│ │ ├── docusaurus.png
│ │ ├── favicon.ico
│ │ ├── favicon.png
│ │ ├── logo.svg
│ │ ├── modals.png
│ │ ├── og.png
│ │ ├── og.svg
│ │ ├── s.png
│ │ ├── site.jpg
│ │ ├── solito.svg
│ │ ├── tutorial
│ │ ├── docsVersionDropdown.png
│ │ └── localeDropdown.png
│ │ ├── undraw_docusaurus_mountain.svg
│ │ ├── undraw_docusaurus_react.svg
│ │ ├── undraw_docusaurus_tree.svg
│ │ ├── v2.png
│ │ └── v2.svg
└── yarn.lock
├── example-monorepos
├── blank
│ ├── .gitignore
│ ├── .vscode
│ │ └── settings.json
│ ├── .yarn
│ │ └── releases
│ │ │ └── yarn-4.7.0.cjs
│ ├── .yarnrc.yml
│ ├── apps
│ │ ├── expo
│ │ │ ├── .gitignore
│ │ │ ├── App.tsx
│ │ │ ├── app-env.d.ts
│ │ │ ├── app.json
│ │ │ ├── babel.config.js
│ │ │ ├── index.js
│ │ │ ├── metro.config.js
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ └── next
│ │ │ ├── .gitignore
│ │ │ ├── app-env.d.ts
│ │ │ ├── app
│ │ │ ├── api
│ │ │ │ └── hello
│ │ │ │ │ └── route.ts
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── styles-provider.tsx
│ │ │ └── users
│ │ │ │ └── [userId]
│ │ │ │ └── page.tsx
│ │ │ ├── next-env.d.ts
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── public
│ │ │ └── vercel.svg
│ │ │ └── tsconfig.json
│ ├── package.json
│ ├── packages
│ │ └── app
│ │ │ ├── features
│ │ │ ├── home
│ │ │ │ └── screen.tsx
│ │ │ └── user
│ │ │ │ └── detail-screen.tsx
│ │ │ ├── index.ts
│ │ │ ├── navigation
│ │ │ └── native
│ │ │ │ └── index.tsx
│ │ │ ├── package.json
│ │ │ ├── provider
│ │ │ ├── index.tsx
│ │ │ ├── navigation
│ │ │ │ ├── index.native.tsx
│ │ │ │ └── index.tsx
│ │ │ └── safe-area
│ │ │ │ ├── index.native.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── use-safe-area.native.ts
│ │ │ │ └── use-safe-area.ts
│ │ │ └── rnw-overrides.d.ts
│ ├── readme.md
│ ├── tsconfig.json
│ ├── turbo.json
│ └── yarn.lock
├── with-custom-font
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── .yarn
│ │ └── releases
│ │ │ └── yarn-3.4.1.cjs
│ ├── .yarnrc.yml
│ ├── apps
│ │ ├── expo
│ │ │ ├── .gitignore
│ │ │ ├── App.tsx
│ │ │ ├── Fonts.tsx
│ │ │ ├── app-env.d.ts
│ │ │ ├── app.json
│ │ │ ├── babel.config.js
│ │ │ ├── index.js
│ │ │ ├── metro.config.js
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ └── next
│ │ │ ├── .gitignore
│ │ │ ├── app-env.d.ts
│ │ │ ├── next-env.d.ts
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── pages
│ │ │ ├── _app.tsx
│ │ │ ├── _document.tsx
│ │ │ ├── index.tsx
│ │ │ └── user
│ │ │ │ └── [id].tsx
│ │ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ ├── font
│ │ │ │ ├── Inter
│ │ │ │ │ ├── Inter-Black.otf
│ │ │ │ │ ├── Inter-BlackItalic.otf
│ │ │ │ │ ├── Inter-Bold.otf
│ │ │ │ │ ├── Inter-BoldItalic.otf
│ │ │ │ │ ├── Inter-ExtraBold.otf
│ │ │ │ │ ├── Inter-ExtraBoldItalic.otf
│ │ │ │ │ ├── Inter-ExtraLight.otf
│ │ │ │ │ ├── Inter-ExtraLightItalic.otf
│ │ │ │ │ ├── Inter-Italic.otf
│ │ │ │ │ ├── Inter-Light.otf
│ │ │ │ │ ├── Inter-LightItalic.otf
│ │ │ │ │ ├── Inter-Medium.otf
│ │ │ │ │ ├── Inter-MediumItalic.otf
│ │ │ │ │ ├── Inter-Regular.otf
│ │ │ │ │ ├── Inter-SemiBold.otf
│ │ │ │ │ ├── Inter-SemiBoldItalic.otf
│ │ │ │ │ ├── Inter-Thin.otf
│ │ │ │ │ ├── Inter-ThinItalic.otf
│ │ │ │ │ └── Inter-V.ttf
│ │ │ │ └── LICENSE.txt
│ │ │ └── vercel.svg
│ │ │ └── tsconfig.json
│ ├── package.json
│ ├── packages
│ │ └── app
│ │ │ ├── features
│ │ │ ├── home
│ │ │ │ └── screen.tsx
│ │ │ └── user
│ │ │ │ └── detail-screen.tsx
│ │ │ ├── index.ts
│ │ │ ├── navigation
│ │ │ └── native
│ │ │ │ └── index.tsx
│ │ │ ├── package.json
│ │ │ ├── provider
│ │ │ ├── dripsy.tsx
│ │ │ ├── index.tsx
│ │ │ ├── navigation
│ │ │ │ ├── index.tsx
│ │ │ │ └── index.web.tsx
│ │ │ └── safe-area
│ │ │ │ ├── index.tsx
│ │ │ │ ├── index.web.tsx
│ │ │ │ ├── use-safe-area.ts
│ │ │ │ └── use-safe-area.web.ts
│ │ │ └── rnw-overrides.d.ts
│ ├── readme.md
│ ├── tsconfig.json
│ ├── turbo.json
│ └── yarn.lock
├── with-expo-router
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── .yarn
│ │ └── releases
│ │ │ └── yarn-3.4.1.cjs
│ ├── .yarnrc.yml
│ ├── apps
│ │ ├── expo
│ │ │ ├── .gitignore
│ │ │ ├── app-env.d.ts
│ │ │ ├── app.json
│ │ │ ├── app
│ │ │ │ ├── _layout.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ └── user
│ │ │ │ │ └── [id].tsx
│ │ │ ├── babel.config.js
│ │ │ ├── metro.config.js
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ └── next
│ │ │ ├── .gitignore
│ │ │ ├── app-env.d.ts
│ │ │ ├── app
│ │ │ ├── api
│ │ │ │ └── hello
│ │ │ │ │ └── route.ts
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ ├── page.tsx
│ │ │ ├── styles-provider.tsx
│ │ │ └── users
│ │ │ │ └── [userId]
│ │ │ │ └── page.tsx
│ │ │ ├── next-env.d.ts
│ │ │ ├── next.config.js
│ │ │ ├── package.json
│ │ │ ├── plugins
│ │ │ └── swc_plugin_reanimated.wasm
│ │ │ ├── public
│ │ │ ├── favicon.ico
│ │ │ └── vercel.svg
│ │ │ └── tsconfig.json
│ ├── package.json
│ ├── packages
│ │ └── app
│ │ │ ├── features
│ │ │ ├── home
│ │ │ │ └── screen.tsx
│ │ │ └── user
│ │ │ │ └── detail-screen.tsx
│ │ │ ├── index.ts
│ │ │ ├── navigation
│ │ │ └── native
│ │ │ │ └── index.tsx
│ │ │ ├── package.json
│ │ │ ├── provider
│ │ │ ├── index.tsx
│ │ │ ├── navigation
│ │ │ │ ├── index.native.tsx
│ │ │ │ └── index.tsx
│ │ │ └── safe-area
│ │ │ │ ├── index.native.tsx
│ │ │ │ ├── index.tsx
│ │ │ │ ├── use-safe-area.native.ts
│ │ │ │ └── use-safe-area.ts
│ │ │ └── rnw-overrides.d.ts
│ ├── readme.md
│ ├── tsconfig.json
│ ├── turbo.json
│ └── yarn.lock
└── with-tailwind
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc.json
│ ├── .yarn
│ └── releases
│ │ └── yarn-3.4.1.cjs
│ ├── .yarnrc.yml
│ ├── apps
│ ├── expo
│ │ ├── .gitignore
│ │ ├── app-env.d.ts
│ │ ├── app.json
│ │ ├── app
│ │ │ ├── _layout.tsx
│ │ │ ├── index.tsx
│ │ │ └── user
│ │ │ │ └── [id].tsx
│ │ ├── babel.config.js
│ │ ├── index.js
│ │ ├── metro.config.js
│ │ ├── package.json
│ │ ├── tailwind.config.js
│ │ └── tsconfig.json
│ └── next
│ │ ├── .gitignore
│ │ ├── app-env.d.ts
│ │ ├── global.css
│ │ ├── next-env.d.ts
│ │ ├── next.config.js
│ │ ├── package.json
│ │ ├── pages
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ ├── index.tsx
│ │ └── user
│ │ │ └── [id].tsx
│ │ ├── plugins
│ │ └── swc_plugin_reanimated.wasm
│ │ ├── postcss.config.js
│ │ ├── public
│ │ ├── favicon.ico
│ │ └── vercel.svg
│ │ ├── tailwind.config.js
│ │ └── tsconfig.json
│ ├── package.json
│ ├── packages
│ └── app
│ │ ├── design
│ │ ├── layout.tsx
│ │ ├── tailwind
│ │ │ └── theme.js
│ │ ├── typography.tsx
│ │ └── view.tsx
│ │ ├── features
│ │ ├── home
│ │ │ └── screen.tsx
│ │ └── user
│ │ │ └── detail-screen.tsx
│ │ ├── index.ts
│ │ ├── nativewind.d.ts
│ │ ├── package.json
│ │ ├── provider
│ │ ├── index.tsx
│ │ └── safe-area
│ │ │ ├── index.tsx
│ │ │ ├── index.web.tsx
│ │ │ ├── use-safe-area.ts
│ │ │ └── use-safe-area.web.ts
│ │ ├── rnw-overrides.d.ts
│ │ ├── tailwind.config.js
│ │ └── tsconfig.json
│ ├── readme.md
│ ├── tsconfig.json
│ ├── turbo.json
│ └── yarn.lock
├── image
├── author
│ ├── index.d.ts
│ └── index.js
├── expo
│ ├── index.d.ts
│ └── index.js
├── index.d.ts
├── index.js
└── react-native-fast-image.js
├── index.d.ts
├── link
├── index.d.ts
└── index.js
├── moti
├── app.js
├── app.ts
├── index.d.ts
└── index.js
├── navigation
├── index.d.ts
└── index.js
├── package.json
├── params.json
├── router
├── index.d.ts
└── index.js
├── scripts
├── install-examples.ts
├── upgrade-examples-expo.ts
└── upgrade-examples.ts
├── src
├── app
│ └── navigation
│ │ ├── index.ts
│ │ ├── use-link.ts
│ │ ├── use-next-params.native.ts
│ │ ├── use-next-params.ts
│ │ ├── use-next-pathname.native.ts
│ │ ├── use-next-pathname.ts
│ │ ├── use-next-router.ts
│ │ ├── use-next-router.web.ts
│ │ ├── use-next-search-params.native.ts
│ │ ├── use-next-search-params.ts
│ │ ├── use-params.ts
│ │ ├── use-pathname.ts
│ │ ├── use-router.ts
│ │ ├── use-search-params.ts
│ │ ├── use-update-search-params.native.ts
│ │ ├── use-update-search-params.ts
│ │ └── use-update-search-params.types.ts
├── helpers
│ ├── merge-refs.ts
│ └── use-stable-callback.ts
├── image
│ ├── author
│ │ └── index.ts
│ ├── context.tsx
│ ├── create-solito-image.tsx
│ ├── default-loader.ts
│ ├── expo
│ │ ├── image.tsx
│ │ ├── image.web.tsx
│ │ └── index.ts
│ ├── fast
│ │ ├── fast.tsx
│ │ ├── fast.web.tsx
│ │ └── index.ts
│ ├── helpers.test.ts
│ ├── helpers.ts
│ ├── image.types.ts
│ ├── types.ts
│ └── use-solito-image.ts
├── index.ts
├── link
│ ├── LinkCoreProps.tsx
│ ├── core.tsx
│ ├── index.tsx
│ ├── link.tsx
│ ├── linking.ts
│ ├── linking.web.ts
│ ├── next-link.tsx
│ ├── next-link.web.tsx
│ ├── text-link.tsx
│ └── use-custom-link.tsx
├── middleware
│ ├── context.tsx
│ ├── context.web.tsx
│ ├── provider.tsx
│ └── types.ts
├── moti
│ ├── app.tsx
│ ├── index.tsx
│ └── link.tsx
├── params
│ ├── index.tsx
│ ├── router.ts
│ ├── router.web.ts
│ ├── use-route.ts
│ ├── use-route.web.ts
│ ├── use-router.ts
│ └── use-router.web.ts
├── router
│ ├── index.ts
│ ├── next-router.ts
│ ├── next-router.web.ts
│ ├── parse-next-path.test.ts
│ ├── parse-next-path.ts
│ ├── replace-helpers.ts
│ ├── replace-helpers.web.ts
│ ├── use-link-to.ts
│ ├── use-link-to.web.ts
│ ├── use-navigation.ts
│ ├── use-navigation.web.ts
│ ├── use-next-router.ts
│ ├── use-next-router.web.ts
│ └── use-router.ts
└── types
│ └── solito-page.tsx
├── tsconfig.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | const lint = require('expo-module-scripts/eslintrc.base.js')
2 | module.exports = {
3 | ...lint,
4 | rules: {
5 | ...lint.rules,
6 | 'react/react-in-jsx-scope': 'off',
7 | '@typescript-eslint/array-type': 'off',
8 | },
9 | }
10 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: nandorojo
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | dist
4 |
5 | .expo
6 | .next
7 |
8 | ios
9 | android
10 |
11 | .test
12 | create-test-app
13 |
14 | .DS_Store
15 |
16 |
17 | .pnp.*
18 | .yarn/*
19 | !.yarn/patches
20 | !.yarn/plugins
21 | !.yarn/releases
22 | !.yarn/sdks
23 | !.yarn/versions
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | example-monorepos
2 | .next
3 | .expo
4 | create-solito-app
5 | docs
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | // generated by @nandorojo/lint-expo
2 | module.exports = require('eslint-config-nando/prettier')
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib",
3 | "explorer.fileNesting.enabled": true,
4 | "explorer.fileNesting.patterns": {
5 | "*.js": "${capture}.js.map, ${capture}.d.ts, ${capture}.d.ts.map",
6 | "*.ts": "$(capture).test.ts, $(capture).benchmark.ts, $(capture).test.tsx, $(capture).test.node.ts, $(capture).test.node.tsx, $(capture).test.native.ts, $(capture).test.native.tsx, $(capture).test.ios.ts, $(capture).test.ios.tsx, $(capture).test.web.ts, $(capture).test.web.tsx, $(capture).test.android.ts, $(capture).test.android.tsx, ${capture}.native.tsx, ${capture}.ios.tsx, ${capture}.android.tsx, ${capture}.web.tsx, ${capture}.native.ts, ${capture}.ios.ts, ${capture}.android.ts, ${capture}.web.ts, ${capture}.native.js, ${capture}.ios.js, ${capture}.android.js, ${capture}.web.js, ${capture}.native.jsx, ${capture}.ios.jsx, ${capture}.android.jsx, ${capture}.web.jsx",
7 | "*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).test.node.ts, $(capture).test.node.tsx, $(capture).test.native.ts, $(capture).test.native.tsx, $(capture).test.ios.ts, $(capture).test.ios.tsx, $(capture).test.web.ts, $(capture).test.web.tsx, $(capture).test.android.ts, $(capture).test.android.tsx, ${capture}.native.tsx, ${capture}.ios.tsx, ${capture}.types.tsx, ${capture}.android.tsx, ${capture}.web.tsx, ${capture}.native.ts, ${capture}.types.ts, ${capture}.ios.ts, ${capture}.android.ts, ${capture}.web.ts, ${capture}.native.js, ${capture}.ios.js, ${capture}.android.js, ${capture}.web.js, ${capture}.native.jsx, ${capture}.ios.jsx, ${capture}.android.jsx, ${capture}.web.jsx"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | nodeLinker: node-modules
2 |
3 | yarnPath: .yarn/releases/yarn-4.7.0.cjs
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Fernando Rojo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | A library dedicated to unifying React Native with Next.js, primarily focused on navigation.
5 |
6 | ```sh
7 | yarn add solito
8 | ```
9 |
10 | Solito is a replacement for my popular `expo-next-react-navigation` library. Consider this the next-generation version, with a brand new API and approach.
11 |
12 | ## Docs & Examples
13 |
14 | - 📚 [Documentation](https://solito.dev)
15 | - 🦄 [Installation](https://solito.dev/install)
16 | - 🐬 [Examples](https://github.com/nandorojo/solito/tree/master/example-monorepos/blank)
17 |
18 | ## Why
19 |
20 | Watch Fernando Rojo's [talk from Next.js Conf 2021](https://www.youtube.com/watch?v=0lnbdRweJtA) to learn more about using React Native with Next.js.
21 |
22 | ## Contributing
23 |
24 | - Add gitmoji: `yarn global add gitmoji-cli` or `npm i -g gitmoji-cli`
25 | - Run `yarn`
26 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 |
4 | // only used for jest, right?
5 | return {
6 | presets: [
7 | ['@babel/preset-env', { targets: { node: 'current' } }],
8 | '@babel/preset-typescript',
9 | ],
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/create-solito-app/.yarn/install-state.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/create-solito-app/.yarn/install-state.gz
--------------------------------------------------------------------------------
/create-solito-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "create-solito-app",
3 | "version": "0.2.0",
4 | "devDependencies": {
5 | "@types/async-retry": "1.4.2",
6 | "@types/cross-spawn": "^6.0.2",
7 | "@types/node": "^12.6.8",
8 | "@types/prompts": "2.0.1",
9 | "@types/rimraf": "3.0.0",
10 | "@types/tar": "6.1.3",
11 | "@types/validate-npm-package-name": "3.0.0",
12 | "@vercel/ncc": "0.33.1",
13 | "async-retry": "1.3.1",
14 | "chalk": "2.4.2",
15 | "commander": "2.20.0",
16 | "cpy": "7.3.0",
17 | "cross-spawn": "6.0.5",
18 | "got": "10.7.0",
19 | "prompts": "2.1.0",
20 | "rimraf": "3.0.0",
21 | "tar": "6.1.12",
22 | "update-check": "1.5.4",
23 | "validate-npm-package-name": "3.0.0"
24 | },
25 | "engines": {
26 | "node": ">=12.22.0"
27 | },
28 | "dependencies": {
29 | "@expo/package-manager": "^0.0.50",
30 | "ts-node": "^10.7.0",
31 | "typescript": "^4.6.2"
32 | },
33 | "scripts": {
34 | "start": "ts-node index.ts",
35 | "test": "rimraf ./create-test-app/ && yarn release && node dist/index.js create-test-app",
36 | "dev": "ncc build ./index.ts -w -o dist/",
37 | "prerelease": "rimraf ./dist/",
38 | "release": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
39 | "prepublish": "yarn release"
40 | },
41 | "bin": "./dist/index.js"
42 | }
43 |
--------------------------------------------------------------------------------
/create-solito-app/readme.md:
--------------------------------------------------------------------------------
1 | # `create-solito-app`
2 |
3 | ```sh
4 | npx create-solito-app@latest
5 | ```
6 |
7 | A script that creates a [solito monorepo](https://github.com/nandorojo/solito/tree/master/example-monorepos/blank) for you in seconds.
8 |
9 | ## Contributing
10 |
11 | - Clone the root repo.
12 | - `cd create-solito-app`
13 | - `yarn`
14 | - `yarn test` to build your local app into a gitignored `create-test-app` folder
15 |
16 | ## Credits
17 |
18 | Thanks to `create-next-app`, I was able to write `create-solito-app` in about an hour.
19 |
--------------------------------------------------------------------------------
/create-solito-app/run.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('child_process').execSync('npx ts-node ./index.ts', {
3 | stdio: 'inherit',
4 | })
5 |
--------------------------------------------------------------------------------
/create-solito-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2019",
4 | "moduleResolution": "node",
5 | "strict": true,
6 | "resolveJsonModule": true,
7 | "esModuleInterop": true,
8 | "skipLibCheck": false
9 | },
10 | "include": ["index.ts"]
11 | }
12 |
--------------------------------------------------------------------------------
/develop/appdir/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/develop/appdir/.yarn/install-state.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/develop/appdir/.yarn/install-state.gz
--------------------------------------------------------------------------------
/develop/appdir/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-4.7.0.cjs
2 |
--------------------------------------------------------------------------------
/develop/appdir/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18 |
19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
20 |
21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
22 |
23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
24 |
25 | ## Learn More
26 |
27 | To learn more about Next.js, take a look at the following resources:
28 |
29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
31 |
32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
33 |
34 | ## Deploy on Vercel
35 |
36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
37 |
38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
39 |
--------------------------------------------------------------------------------
/develop/appdir/app/api/hello/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET(request: Request) {
2 | return new Response('Hello, Solito!')
3 | }
4 |
--------------------------------------------------------------------------------
/develop/appdir/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/develop/appdir/app/favicon.ico
--------------------------------------------------------------------------------
/develop/appdir/app/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #__next {
4 | width: 100%;
5 | /* To smooth any scrolling behavior */
6 | -webkit-overflow-scrolling: touch;
7 | margin: 0px;
8 | padding: 0px;
9 | /* Allows content to fill the viewport and go beyond the bottom */
10 | min-height: 100%;
11 | }
12 | #__next {
13 | flex-shrink: 0;
14 | flex-basis: auto;
15 | flex-direction: column;
16 | flex-grow: 1;
17 | display: flex;
18 | flex: 1;
19 | }
20 | html {
21 | scroll-behavior: smooth;
22 | /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
23 | -webkit-text-size-adjust: 100%;
24 | height: 100%;
25 | }
26 | body {
27 | display: flex;
28 | /* Allows you to scroll below the viewport; default value is visible */
29 | overflow-y: auto;
30 | overscroll-behavior-y: none;
31 | text-rendering: optimizeLegibility;
32 | -webkit-font-smoothing: antialiased;
33 | -moz-osx-font-smoothing: grayscale;
34 | -ms-overflow-style: scrollbar;
35 | }
36 |
--------------------------------------------------------------------------------
/develop/appdir/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { StylesProvider } from './styles-provider'
2 | import './globals.css'
3 |
4 | export const metadata = {
5 | title: 'Create Solito App',
6 | description: 'Generated by create Solito app',
7 | }
8 |
9 | export default function RootLayout({
10 | children,
11 | }: {
12 | children: React.ReactNode
13 | }) {
14 | return (
15 |
16 |
17 | {children}
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/develop/appdir/app/page.js:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { Text, View } from 'react-native'
3 | import { MotiLink } from 'solito/moti/app'
4 |
5 | export default function Home() {
6 | return (
7 |
8 |
19 | Hello, Next.js App Router.
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/develop/appdir/app/styles-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useServerInsertedHTML } from 'next/navigation'
3 | import { StyleSheet } from 'react-native'
4 |
5 | export function StylesProvider({ children }: { children: React.ReactNode }) {
6 | useServerInsertedHTML(() => {
7 | // @ts-ignore
8 | const sheet = StyleSheet.getSheet()
9 | return (
10 |
14 | )
15 | })
16 | return <>{children}>
17 | }
18 |
--------------------------------------------------------------------------------
/develop/appdir/app/users/[userId]/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { Text, View } from 'react-native'
3 | import { useParams, useRouter, useSearchParams } from 'solito/navigation'
4 |
5 | const useUserParams = useParams<{ userId: string }>
6 |
7 | export default function Home() {
8 | const { userId } = useUserParams()
9 | const router = useRouter()
10 | const searchParams = useSearchParams()
11 | return (
12 |
13 | router.back()}>
14 | {userId}, here is the search param: {searchParams?.get('search')}
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/develop/appdir/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "appdir",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@types/node": "20.2.5",
13 | "@types/react": "18.2.4",
14 | "moti": "^0.30.0",
15 | "next": "^15.2.3",
16 | "react": "19.0.0-rc.1",
17 | "react-dom": "19.0.0-rc.1",
18 | "react-native-reanimated": "4.0.0-beta.2",
19 | "react-native-web": "^0.19.12",
20 | "solito": "^4.4.0"
21 | },
22 | "devDependencies": {
23 | "react-native": "^0.78.0",
24 | "typescript": "^5.8.2"
25 | },
26 | "packageManager": "yarn@4.7.0"
27 | }
28 |
--------------------------------------------------------------------------------
/develop/appdir/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/develop/appdir/public/thirteen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/develop/appdir/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/develop/appdir/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules", "../node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/develop/next/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/develop/next/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = require('@expo/next-adapter').withExpo(
2 | {
3 | transpilePackages: [
4 | 'react-native-web',
5 | 'react-native',
6 | '@expo/next-adapter',
7 | 'solito',
8 | ],
9 | reactStrictMode: false,
10 | },
11 | {
12 | projectRoot: __dirname,
13 | }
14 | )
15 |
--------------------------------------------------------------------------------
/develop/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "@expo/next-adapter": "^4.0.13",
4 | "next": "13.4.4",
5 | "react": "18.2.0",
6 | "react-dom": "18.2.0",
7 | "solito": "canary"
8 | },
9 | "devDependencies": {
10 | "@types/node": "^18.11.9",
11 | "@types/react": "18.0.25",
12 | "next-transpile-modules": "^10.0.0",
13 | "react-native-web": "^0.18.10",
14 | "typescript": "^4.9.3"
15 | },
16 | "resolutions": {
17 | "react": "18.2.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/develop/next/pages/cover.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | import { SolitoImage } from 'solito/image'
3 |
4 | export default function CoverPage() {
5 | return (
6 |
7 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/develop/next/pages/import.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | import { SolitoImage } from 'solito/image'
3 |
4 | import mountains from '../public/mountains.jpg'
5 |
6 | export default function ImportPage() {
7 | return (
8 |
9 |
18 |
19 | )
20 | }
21 |
--------------------------------------------------------------------------------
/develop/next/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | import { SolitoImage } from 'solito/image'
3 |
4 | export default function Home() {
5 | return (
6 |
12 |
24 |
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/develop/next/public/mountains.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/develop/next/public/mountains.jpg
--------------------------------------------------------------------------------
/develop/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "es5",
5 | "lib": ["dom", "dom.iterable", "esnext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "strict": false,
9 | "forceConsistentCasingInFileNames": true,
10 | "noEmit": true,
11 | "incremental": true,
12 | "esModuleInterop": true,
13 | "module": "esnext",
14 | "moduleResolution": "node",
15 | "resolveJsonModule": true,
16 | "isolatedModules": true,
17 | "jsx": "preserve"
18 | },
19 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
20 | "exclude": ["node_modules"]
21 | }
22 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/docs/.yarn/install-state.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/.yarn/install-state.gz
--------------------------------------------------------------------------------
/docs/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-4.1.0.cjs
2 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
4 |
5 | ### Installation
6 |
7 | ```
8 | $ yarn
9 | ```
10 |
11 | ### Local Development
12 |
13 | ```
14 | $ yarn start
15 | ```
16 |
17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
18 |
19 | ### Build
20 |
21 | ```
22 | $ yarn build
23 | ```
24 |
25 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
26 |
27 | ### Deployment
28 |
29 | Using SSH:
30 |
31 | ```
32 | $ USE_SSH=true yarn deploy
33 | ```
34 |
35 | Not using SSH:
36 |
37 | ```
38 | $ GIT_USER= yarn deploy
39 | ```
40 |
41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42 |
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/docs/community.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Community
3 | ---
4 |
--------------------------------------------------------------------------------
/docs/docs/expo-router.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Expo Router
3 | ---
4 |
5 | > This starter is outdated. We recommend using the [standard Solito starter](/starter) and using this monorepo as a reference for where to put Expo router-specific files.
6 |
7 | You can see the monorepo and what comes installed [here](https://github.com/nandorojo/solito/tree/master/example-monorepos/with-expo-router).
8 |
9 | ## Start from Vercel
10 |
11 | Click this button and you can instantly deploy the starter project to Vercel for free. Deploying to Vercel will create a new git repo for you too, so you don't need to clone it from the terminal.
12 |
13 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnandorojo%2Fsolito%2Ftree%2Fmaster%2Fexample-monorepos%2Fwith-expo-router&project-name=solito-app&repo-name=solito-app&demo-title=Solito%20App%20%E2%9A%A1%EF%B8%8F&demo-description=React%20Native%20%2B%20Next.js%20starter%20with%20Solito.%20Made%20by%20Fernando%20Rojo.&demo-url=https%3A%2F%2Fsolito.dev%2Fstarter&demo-image=https%3A%2F%2Fsolito.dev%2Fimg%2Fog.png&root-directory=apps%2Fnext&build-command=cd+..%2F..%3Bnpx+turbo+run+build+--filter%3Dnext-app)
14 |
15 | ## Start from the terminal
16 |
17 | You can install the starter monorepo using the CLI if you prefer:
18 |
19 | ```sh
20 | npx create-solito-app@latest -t with-expo-router
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/docs/guides/expo-router.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Expo Router
3 | ---
4 |
5 | Expo recently announced an experimental new routing system which lets you use file-system based routing in native apps. The API is very Next.js- and Remix-esque. I view it as the future of cross-platform routing.
6 |
7 | To use Solito with [Expo Router](https://expo.github.io/router/), you'll need to follow their [Getting Started](https://expo.github.io/router/docs/#getting-started) guide. These steps should be done inside of `apps/expo` in your Solito project.
8 |
9 | Once those steps are done, you're good to go. You can create files inside of `apps/expo/app` to use file system routing on iOS and Android. Solito's `useLink` hook and `Link` component will work like always.
10 |
11 | ## Starter app
12 |
13 | Run this from the terminal to get a Solito + Expo Router project.
14 |
15 | ```sh
16 | npx create-solito-app@latest -t with-expo-router
17 | ```
18 |
19 | ## In an existing app
20 |
21 | To use Expo Router in an existing Solito project, you'll have to remove the `Navigation` provider from `packages/app/providers/index.tsx`. Expo Router will provide this context for you, reducing boilerplate.
22 |
23 | You also need to remove any `@react-navigation` packages from your `package.json` files to avoid conflicts.
24 |
--------------------------------------------------------------------------------
/docs/docs/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Installation
3 | id: install
4 | ---
5 |
6 | :::tip
7 |
8 | Clone/reference the [starter monorepo](/starter) to have Solito pre-installed for you.
9 |
10 | :::
11 |
12 | ## Install the package
13 |
14 | ```sh
15 | yarn add solito
16 | ```
17 |
18 | Or, with NPM:
19 |
20 | ```sh
21 | npm i solito
22 | ```
23 |
24 | ## Next.js setup
25 |
26 | You might need `next-transpile-modules` to run this in your Next.js app, unless `transpilePackages` works for you. It's been known to have issues with Reanimated, so it depends on whether or not you're using Reanimated.
27 |
28 | If you're in a monorepo (which is [recommended](/starter)), start by entering the directory of your Next.js app.
29 |
30 | ```sh
31 | cd apps/next
32 | ```
33 |
34 | Next, install the peer dependencies.
35 |
36 | ```
37 | yarn add -D next-transpile-modules next-compose-plugins
38 | ```
39 |
40 | Then run `yarn` in the root of your monorepo.
41 |
42 | Finally, add `solito` to `next-transpile-modules`.
43 |
44 | Your `next.config.js` file should look something like this:
45 |
46 | ```js
47 | const { withExpo } = require('@expo/next-adapter')
48 | const withPlugins = require('next-compose-plugins')
49 |
50 | const withTM = require('next-transpile-modules')([
51 | 'solito',
52 | // add other packages here that need transpiling, such as moti
53 | ])
54 |
55 | module.exports = withPlugins([withTM, [withExpo, { projectRoot: __dirname }]], {
56 | // your next config goes here ...
57 | })
58 | ```
59 |
--------------------------------------------------------------------------------
/docs/docs/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | ---
4 |
5 | # Tutorial Intro
6 |
7 | Let's discover **Docusaurus in less than 5 minutes**.
8 |
9 | ## Getting Started
10 |
11 | Get started by **creating a new site**.
12 |
13 | Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
14 |
15 | ## Generate a new site
16 |
17 | Generate a new Docusaurus site using the **classic template**:
18 |
19 | ```shell
20 | npm init docusaurus@latest my-website classic
21 | ```
22 |
23 | ## Start your site
24 |
25 | Run the development server:
26 |
27 | ```shell
28 | cd my-website
29 |
30 | npx docusaurus start
31 | ```
32 |
33 | Your site starts at `http://localhost:3000`.
34 |
35 | Open `docs/intro.md` and edit some lines: the site **reloads automatically** and displays your changes.
36 |
--------------------------------------------------------------------------------
/docs/docs/moti-pressable.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Moti Integration
3 | id: moti-pressable
4 | ---
5 |
6 | [`moti`](https://moti.fyi) is a popular animation library for React Native, powered by Reanimated 2. It works on iOS, Android and Web.
7 |
8 | One useful component from `moti` is `MotiPressable`. It works just like the `Pressable` from React Native, with the added ability to animate based on hover and pressed states.
9 |
10 | If you're using `MotiPressable` for your links, you should pair it with `solito`'s `useLink` hook, instead of the `` component.
11 |
12 | `useLink` is inspired by `useLinkProps` from React Navigation.
13 |
14 | ## Example
15 |
16 | Here is an example of how you can create an accessible, implemented with `MotiPressable`.
17 |
18 | ```tsx twoslash
19 | import { MotiPressable } from 'moti/interactions'
20 | import { useLink, UseLinkProps } from 'solito/link'
21 |
22 | type MotiLinkProps = UseLinkProps &
23 | Omit, keyof UseLinkProps>
24 |
25 | export function MotiLink({
26 | as,
27 | href,
28 | shallow,
29 | children,
30 | ...motiPressableProps
31 | }: MotiLinkProps) {
32 | const linkProps = useLink({
33 | as,
34 | href,
35 | shallow,
36 | })
37 |
38 | return (
39 |
40 | {children}
41 |
42 | )
43 | }
44 | ```
45 |
--------------------------------------------------------------------------------
/docs/docs/recipes/deploying.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Deploying
3 | ---
4 |
5 | Using both Github Actions and the tools provided by companies such as Vercel, we can deploy both our web app and update our mobile app on each commit.
6 |
7 |
8 | ## Web
9 |
10 | Deploying to [Vercel](https://vercel.com) works out of the box when you link your project via GitHub.
11 |
12 |
13 | ## Expo
14 |
15 | While Expo doesn't have a GitHub integration like Vercel, they do offer documentation for setting up deployments via Github Action to publish on each commit: [Expo Github Actions](https://docs.expo.dev/eas-update/github-actions/)
16 |
17 | However, Expo's docs don't account for a monorepo structure. By adding in `working-directory` to the Expo Github action, you can run `eas update` in the correct directory.
18 |
19 | ```yml title=".github/workflows/update.yml"
20 | - name: Publish update
21 | run: eas update --auto
22 | working-directory: ./apps/expo
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/docs/recipes/platform-code.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Platform-Specific Code
3 | ---
4 |
5 | You can use different code on different platforms by specifying different file extensions.
6 |
7 | See the [tree shaking](/recipes/tree-shaking) guide for more.
8 |
9 | ## Video
10 |
11 | For a video walkthrough of using different code per-platform, see my 2021 Next.js Conf talk at about 21:53:
12 |
13 |
22 |
23 | ## Example
24 |
25 | One example of platform-specific code is using `@react-native-firebase` on Native, and `firebase` on Web.
26 |
27 | To see how this works, you can reference the [source code](https://github.com/nandorojo/nextjs-conf-22-example/tree/setup/packages/app/features/auth/firebase) from my 2022 Next.js Conf talk. Here's the Firebase section.
28 |
--------------------------------------------------------------------------------
/docs/docs/recipes/redirects.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Redirects
3 | ---
4 |
5 | Imagine you want users to enter the URL `/@drake`, and for it to redirect to the page at `/artists/[slug].tsx`.
6 |
7 | Next.js lets you handle this easily with redirects and rewrites inside of `next.config.js`
8 |
9 | ```js twoslash
10 | // next.config.js
11 |
12 | module.exports = {
13 | async rewrites() {
14 | return [
15 | {
16 | source: '/@:slug',
17 | destination: '/artists/:slug',
18 | },
19 | ]
20 | },
21 | }
22 | ```
23 |
24 | The issue is, this won't work out of the box with React Navigation.
25 |
26 | In order to achieve the same, we'll need to edit the [React Navigation linking config](https://reactnavigation.org/docs/configuring-links).
27 |
28 | ```tsx twoslash
29 | import * as Linking from 'expo-linking'
30 | import { getStateFromPath, LinkingOptions } from '@react-navigation/native'
31 |
32 | const withRewrites = (unparsedPath: string): string => {
33 | if (unparsedPath.startsWith('/@')) {
34 | const slug = unparsedPath.replace('/@', '').split('?')[0].split('/')[0]
35 | const rest = unparsedPath.replace(`/@${slug}`, '')
36 |
37 | return `/artists/${slug}${rest}`
38 | }
39 |
40 | // you can put other redirects here
41 |
42 | return unparsedPath
43 | }
44 |
45 | const linking: LinkingOptions = {
46 | // ...your linking config
47 | prefixes: [Linking.createURL('/'), 'https://fernandorojo.co'],
48 | getStateFromPath(path, config) {
49 | const finalPath = withRewrites(path)
50 |
51 | return getStateFromPath(finalPath, config)
52 | },
53 | }
54 | ```
55 |
--------------------------------------------------------------------------------
/docs/docs/recipes/tree-shaking.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Tree Shaking
3 | ---
4 |
5 | Every time you use React Navigation code, you can replace it with an empty function to use on Web.
6 |
7 | This will avoid importing unused code on Web, and prevent unnecessary errors from not having a parent context.
8 |
9 | In order to do so, we will re-export each module we use from the `react-navigation` package, and give it a no-op on Web.
10 |
11 | ## Create a platform-specific module
12 |
13 | Say you want to use `useScrollToTop()`. Create a file and re-export `useScrollToTop` from `react-navigation` for Native and give it a no-op on Web.
14 |
15 | #### `hooks/use-scroll-to-top.ts`
16 |
17 | ```tsx
18 | export { useScrollToTop } from '@react-navigation/native'
19 | ```
20 |
21 | Next, make the same file with a `.web.ts` extension:
22 |
23 | #### `hooks/use-scroll-to-top.web.ts`
24 |
25 | ```tsx
26 | /**
27 | * @deprecated you are importing from the wrong file.
28 | */
29 | export function useScrollToTop() {
30 | // no-op
31 | }
32 | ```
33 |
34 | Next, replace your imports from `@react-navigation/native` with `hooks/use-scroll-to-top`:
35 |
36 | ```tsx
37 | import { useScrollToTop } from 'hooks/use-scroll-to-top'
38 | ```
39 |
40 | Solito uses this approach under the hood, ensuring that code is all tree-shaken for the platform it's meant to run on.
41 |
--------------------------------------------------------------------------------
/docs/docs/starter.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Starter Project
3 | ---
4 |
5 | To get you up and running instantly, I created a starter monorepo with all the annoying config done for you.
6 |
7 | You can see the monorepo and what comes installed [here](https://github.com/nandorojo/solito/tree/master/example-monorepos/blank).
8 |
9 | ## Start from Vercel
10 |
11 | Click this button and you can instantly deploy the starter project to Vercel for free. Deploying to Vercel will create a new git repo for you too, so you don't need to clone it from the terminal.
12 |
13 | [](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnandorojo%2Fsolito%2Ftree%2Fmaster%2Fexample-monorepos%2Fblank&env=ENABLE_ROOT_PATH_BUILD_CACHE&envDescription=Set%20this%20environment%20variable%20to%201%20for%20Turborepo%20to%20cache%20your%20node_modules.&envLink=https%3A%2F%2Ftwitter.com%2Fjaredpalmer%2Fstatus%2F1488954563533189124&project-name=solito-app&repo-name=solito-app&demo-title=Solito%20App%20%E2%9A%A1%EF%B8%8F&demo-description=React%20Native%20%2B%20Next.js%20starter%20with%20Solito.%20Made%20by%20Fernando%20Rojo.&demo-url=https%3A%2F%2Fsolito.dev%2Fstarter&demo-image=https%3A%2F%2Fsolito.dev%2Fimg%2Fog.png&root-directory=apps%2Fnext&build-command=cd+..%2F..%3Bnpx+turbo+run+build+--filter%3Dnext-app)
14 |
15 | ## Start from the terminal
16 |
17 | You can install the starter monorepo using the CLI if you prefer:
18 |
19 | ```sh
20 | npx create-solito-app@latest my-solito-app
21 | ```
22 |
--------------------------------------------------------------------------------
/docs/docs/typescript/navigation-options.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Navigation Options
3 | ---
4 |
5 | You may want to implement a consistent header on Web across pages. For this use case, check out the [custom layout](#) and [Web Header](#) tutorials.
6 |
7 | What those tutorials don't touch on is getting the types right.
8 |
9 | Solito's `SolitoPage` type comes with an optional `navigationOptions` property.
10 |
11 | ```tsx
12 | import type { SolitoPage } from 'solito'
13 |
14 | const ArtistsPage: SolitoPage = () => {
15 | return <>>
16 | }
17 |
18 | ArtistsPage.navigationOptions = (router) => {
19 | return {
20 | // you can set your own options here
21 | }
22 | }
23 | ```
24 |
25 | So, where do the types for `navigationOptions` come from?
26 |
27 | Rather than restrict you to some type we I came up with, I decided to let you set your own `navigationOptions` type.
28 |
29 | Simply add this inside of any typescript file:
30 |
31 | ```tsx
32 | // src/types.ts
33 |
34 | type MyOptions = {
35 | title: string
36 | }
37 |
38 | declare module 'solito' {
39 | interface SolitoCustomNavigationOptions extends MyOptions {}
40 | }
41 | ```
42 |
43 | And now, you will get autocomplete for `title` inside of any solito page's `navigationOptions`.
44 |
45 | ## With React Navigation types
46 |
47 | If you plan on using React Navigation's `Header` inside of `_app.tsx`, then you could use the header's props as your options:
48 |
49 | ```tsx
50 | import { Header } from '@react-navigation/elements'
51 | import type { SolitoAppProps } from 'solito'
52 |
53 | type HeaderProps = React.ComponentProps
54 |
55 | declare module 'solito' {
56 | interface SolitoCustomNavigationOptions extends HeaderProps {}
57 | }
58 |
59 | export default function App({ Component, pageProps, router }: SolitoAppProps) {
60 | const navigationOptions =
61 | typeof Component.navigationOptions === 'function'
62 | ? Component.navigationOptions(router)
63 | : Component.navigationOptions
64 |
65 | return (
66 | <>
67 |
68 |
69 | >
70 | )
71 | }
72 | ```
73 |
--------------------------------------------------------------------------------
/docs/docs/typescript/next-replacements.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Next.js Replacements
3 | ---
4 |
5 | `solito` provides a number of drop-in replacements for Next.js types.
6 |
7 | Namely, `SolitoAppProps` instead of `AppProps`, and `SolitoPage` instead of `NextPage`.
8 |
9 | These types extend the next types, and add additional options.
10 |
--------------------------------------------------------------------------------
/docs/docs/usage/text-link.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: TextLink
3 | sidebar_label:
4 | ---
5 |
6 | ```tsx
7 | import { TextLink } from 'solito/link'
8 | ```
9 |
10 | A drop-in replacement for [Next.js' `` component](https://nextjs.org/docs/api-reference/next/link). It follows the exact same API.
11 |
12 | The difference from Solito's [``](/usage/link) component is that this component accepts `Text` nodes as children.
13 |
14 | ```tsx
15 | Home
16 | ```
17 |
18 | You can also put nested `Text` components inside:
19 |
20 | ```tsx
21 |
22 | Go Home
23 |
24 | ```
25 |
26 | ## Props
27 |
28 | ### `textProps`
29 |
30 | Props used by the underlying `Text` component.
31 |
32 | ```tsx
33 |
34 | Link to Home
35 |
36 | ```
37 |
38 | ### Next.js props
39 |
40 | It also supports the props from [Next.js' `` component](https://nextjs.org/docs/api-reference/next/link), besides `passHref`.
41 |
42 | ## Configuration
43 |
44 | Before a `Link` can work on iOS and Android, you'll need to properly configure your `linking` config with React Navigation.
45 |
46 | See their docs:
47 |
48 | - [Configuring links](https://reactnavigation.org/docs/configuring-links/) for in-app routing
49 | - [Deep linking](https://reactnavigation.org/docs/deep-linking/) for handling inbound links into your app
50 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids"
15 | },
16 | "dependencies": {
17 | "@docusaurus/core": "2.0.0-beta.14",
18 | "@docusaurus/preset-classic": "2.0.0-beta.14",
19 | "@mdx-js/react": "^1.6.21",
20 | "@react-navigation/native": "7.0.0",
21 | "@types/react-native": "^0.67.2",
22 | "clsx": "^1.1.1",
23 | "docusaurus-preset-shiki-twoslash": "^1.1.37",
24 | "dripsy": "^3.6.0",
25 | "expo-linking": "^3.0.0",
26 | "moti": "^0.17.1",
27 | "next": "13.4.4",
28 | "prism-react-renderer": "^1.2.1",
29 | "react": "^17.0.1",
30 | "react-dom": "^17.0.1",
31 | "solito": "latest"
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.5%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 1 chrome version",
41 | "last 1 firefox version",
42 | "last 1 safari version"
43 | ]
44 | },
45 | "packageManager": "yarn@4.1.0"
46 | }
47 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import clsx from 'clsx';
3 | import styles from './HomepageFeatures.module.css';
4 |
5 | const FeatureList = [
6 | {
7 | title: 'Easy to Use',
8 | Svg: require('../../static/img/undraw_docusaurus_mountain.svg').default,
9 | description: (
10 | <>
11 | Docusaurus was designed from the ground up to be easily installed and
12 | used to get your website up and running quickly.
13 | >
14 | ),
15 | },
16 | {
17 | title: 'Focus on What Matters',
18 | Svg: require('../../static/img/undraw_docusaurus_tree.svg').default,
19 | description: (
20 | <>
21 | Docusaurus lets you focus on your docs, and we'll do the chores. Go
22 | ahead and move your docs into the docs
directory.
23 | >
24 | ),
25 | },
26 | {
27 | title: 'Powered by React',
28 | Svg: require('../../static/img/undraw_docusaurus_react.svg').default,
29 | description: (
30 | <>
31 | Extend or customize your website layout by reusing React. Docusaurus can
32 | be extended while reusing the same header and footer.
33 | >
34 | ),
35 | },
36 | ];
37 |
38 | function Feature({Svg, title, description}) {
39 | return (
40 |
41 |
42 |
43 |
44 |
45 |
{title}
46 |
{description}
47 |
48 |
49 | );
50 | }
51 |
52 | export default function HomepageFeatures() {
53 | return (
54 |
55 |
56 |
57 | {FeatureList.map((props, idx) => (
58 |
59 | ))}
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/docs/src/components/HomepageFeatures.module.css:
--------------------------------------------------------------------------------
1 | .features {
2 | display: flex;
3 | align-items: center;
4 | padding: 2rem 0;
5 | width: 100%;
6 | }
7 |
8 | .featureSvg {
9 | height: 200px;
10 | width: 200px;
11 | }
12 |
--------------------------------------------------------------------------------
/docs/src/pages/index.module.css:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS files with the .module.css suffix will be treated as CSS modules
3 | * and scoped locally.
4 | */
5 |
6 | .heroBanner {
7 | padding: 4rem 0;
8 | text-align: center;
9 | position: relative;
10 | overflow: hidden;
11 | }
12 |
13 | @media screen and (max-width: 966px) {
14 | .heroBanner {
15 | padding: 2rem;
16 | }
17 | }
18 |
19 | .buttons {
20 | display: flex;
21 | align-items: center;
22 | justify-content: center;
23 | }
24 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/font/Satoshi-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/font/Satoshi-Bold.ttf
--------------------------------------------------------------------------------
/docs/static/font/Satoshi-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/font/Satoshi-Italic.ttf
--------------------------------------------------------------------------------
/docs/static/font/Satoshi-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/font/Satoshi-Medium.ttf
--------------------------------------------------------------------------------
/docs/static/font/Satoshi-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/font/Satoshi-MediumItalic.ttf
--------------------------------------------------------------------------------
/docs/static/font/Satoshi-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/font/Satoshi-Regular.ttf
--------------------------------------------------------------------------------
/docs/static/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/4.png
--------------------------------------------------------------------------------
/docs/static/img/app.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/app.PNG
--------------------------------------------------------------------------------
/docs/static/img/docusaurus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/docusaurus.png
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/favicon.png
--------------------------------------------------------------------------------
/docs/static/img/modals.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/modals.png
--------------------------------------------------------------------------------
/docs/static/img/og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/og.png
--------------------------------------------------------------------------------
/docs/static/img/s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/s.png
--------------------------------------------------------------------------------
/docs/static/img/site.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/site.jpg
--------------------------------------------------------------------------------
/docs/static/img/tutorial/docsVersionDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/tutorial/docsVersionDropdown.png
--------------------------------------------------------------------------------
/docs/static/img/tutorial/localeDropdown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/tutorial/localeDropdown.png
--------------------------------------------------------------------------------
/docs/static/img/v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/docs/static/img/v2.png
--------------------------------------------------------------------------------
/example-monorepos/blank/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | */fastlane/report.xml
52 | */fastlane/Preview.html
53 | */fastlane/screenshots
54 |
55 | # Bundle artifacts
56 | *.jsbundle
57 |
58 | # CocoaPods
59 | /ios/Pods/
60 |
61 | # Expo
62 | .expo/*
63 | web-build/
64 |
65 | **/*/.expo
66 |
67 | **/*/.next
68 |
69 | apps/expo/ios
70 | apps/expo/android
71 |
72 | .turbo
73 | build/**
74 |
75 | .pnp.*
76 | .yarn/*
77 | !.yarn/patches
78 | !.yarn/plugins
79 | !.yarn/releases
80 | !.yarn/sdks
81 | !.yarn/versions
--------------------------------------------------------------------------------
/example-monorepos/blank/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "explorer.fileNesting.enabled": true,
3 | "explorer.fileNesting.patterns": {
4 | "*.js": "${capture}.js.map, ${capture}.d.ts, ${capture}.d.ts.map",
5 | "*.ts": "$(capture).test.ts, $(capture).benchmark.ts, $(capture).test.tsx, $(capture).test.node.ts, $(capture).test.node.tsx, $(capture).test.native.ts, $(capture).test.native.tsx, $(capture).test.ios.ts, $(capture).test.ios.tsx, $(capture).test.web.ts, $(capture).test.web.tsx, $(capture).test.android.ts, $(capture).test.android.tsx, ${capture}.native.tsx, ${capture}.ios.tsx, ${capture}.android.tsx, ${capture}.web.tsx, ${capture}.native.ts, ${capture}.ios.ts, ${capture}.android.ts, ${capture}.web.ts, ${capture}.native.js, ${capture}.ios.js, ${capture}.android.js, ${capture}.web.js, ${capture}.native.jsx, ${capture}.ios.jsx, ${capture}.android.jsx, ${capture}.web.jsx",
6 | "*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).test.node.ts, $(capture).test.node.tsx, $(capture).test.native.ts, $(capture).test.native.tsx, $(capture).test.ios.ts, $(capture).test.ios.tsx, $(capture).test.web.ts, $(capture).test.web.tsx, $(capture).test.android.ts, $(capture).test.android.tsx, ${capture}.native.tsx, ${capture}.ios.tsx, ${capture}.types.tsx, ${capture}.android.tsx, ${capture}.web.tsx, ${capture}.native.ts, ${capture}.types.ts, ${capture}.ios.ts, ${capture}.android.ts, ${capture}.web.ts, ${capture}.native.js, ${capture}.ios.js, ${capture}.android.js, ${capture}.web.js, ${capture}.native.jsx, ${capture}.ios.jsx, ${capture}.android.jsx, ${capture}.web.jsx"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example-monorepos/blank/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | compressionLevel: mixed
2 |
3 | enableGlobalCache: false
4 |
5 | nodeLinker: node-modules
6 |
7 | yarnPath: .yarn/releases/yarn-4.7.0.cjs
8 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/App.tsx:
--------------------------------------------------------------------------------
1 | import { NativeNavigation } from 'app/navigation/native'
2 | import { Provider } from 'app/provider'
3 |
4 | export default function App() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "solito-blank",
4 | "slug": "solito-blank",
5 | "version": "1.0.0",
6 | "scheme": "solito-blank",
7 | "platforms": ["ios", "android"],
8 | "ios": {
9 | "bundleIdentifier": "com.solito.blank"
10 | },
11 | "newArchEnabled": true,
12 | "experiments": {
13 | "reactCanary": true,
14 | "reactCompiler": true
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 | return {
4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]],
5 | plugins: ['react-native-reanimated/plugin'],
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo'
2 |
3 | import App from './App'
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App)
9 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.dev/guides/monorepos
2 | // Learn more https://docs.expo.io/guides/customizing-metro
3 | /**
4 | * @type {import('expo/metro-config')}
5 | */
6 | const { getDefaultConfig } = require('expo/metro-config')
7 | const path = require('path')
8 |
9 | const projectRoot = __dirname
10 | const workspaceRoot = path.resolve(projectRoot, '../..')
11 |
12 | const config = getDefaultConfig(projectRoot)
13 |
14 | config.watchFolders = [workspaceRoot]
15 | config.resolver.nodeModulesPaths = [
16 | path.resolve(projectRoot, 'node_modules'),
17 | path.resolve(workspaceRoot, 'node_modules'),
18 | ]
19 | config.resolver.disableHierarchicalLookup = true
20 |
21 | config.transformer.getTransformOptions = async () => ({
22 | transform: {
23 | experimentalImportSupport: false,
24 | inlineRequires: true,
25 | },
26 | })
27 |
28 | module.exports = config
29 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "app": "*",
4 | "expo": "52.0.38",
5 | "expo-image": "~2.0.6",
6 | "expo-linear-gradient": "~14.0.2",
7 | "expo-linking": "~7.0.5",
8 | "expo-splash-screen": "~0.29.22",
9 | "expo-status-bar": "~2.0.1",
10 | "moti": "^0.30.0",
11 | "react-native": "0.76.7",
12 | "react-native-gesture-handler": "~2.20.2",
13 | "react-native-reanimated": "3.16.1",
14 | "react-native-safe-area-context": "4.12.0",
15 | "react-native-screens": "~4.4.0"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.24.0",
19 | "@types/react": "~18.3.12",
20 | "babel-plugin-react-compiler": "19.0.0-beta-3229e95-20250315",
21 | "react-compiler-runtime": "19.0.0-beta-3229e95-20250315",
22 | "typescript": "~5.3.3"
23 | },
24 | "scripts": {
25 | "start": "expo start",
26 | "android": "expo run:android",
27 | "ios": "expo run:ios"
28 | },
29 | "main": "index.js",
30 | "version": "1.0.0",
31 | "private": true,
32 | "name": "expo-app",
33 | "expo": {
34 | "install": {
35 | "exclude": [
36 | "react"
37 | ]
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/api/hello/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET(request: Request) {
2 | return new Response('Hello, Solito!')
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/blank/apps/next/app/favicon.ico
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #__next {
4 | width: 100%;
5 | /* To smooth any scrolling behavior */
6 | -webkit-overflow-scrolling: touch;
7 | margin: 0px;
8 | padding: 0px;
9 | /* Allows content to fill the viewport and go beyond the bottom */
10 | min-height: 100%;
11 | }
12 | #__next {
13 | flex-shrink: 0;
14 | flex-basis: auto;
15 | flex-direction: column;
16 | flex-grow: 1;
17 | display: flex;
18 | flex: 1;
19 | }
20 | html {
21 | scroll-behavior: smooth;
22 | /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
23 | -webkit-text-size-adjust: 100%;
24 | height: 100%;
25 | }
26 | body {
27 | display: flex;
28 | /* Allows you to scroll below the viewport; default value is visible */
29 | overflow-y: auto;
30 | overscroll-behavior-y: none;
31 | text-rendering: optimizeLegibility;
32 | -webkit-font-smoothing: antialiased;
33 | -moz-osx-font-smoothing: grayscale;
34 | -ms-overflow-style: scrollbar;
35 | }
36 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { StylesProvider } from './styles-provider'
2 | import './globals.css'
3 |
4 | export const metadata = {
5 | title: 'Create Solito App',
6 | description: 'Generated by create Solito app',
7 | }
8 |
9 | export default function RootLayout({
10 | children,
11 | }: {
12 | children: React.ReactNode
13 | }) {
14 | return (
15 |
16 |
17 | {children}
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export { HomeScreen as default } from 'app/features/home/screen'
4 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/styles-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useServerInsertedHTML } from 'next/navigation'
3 | import { StyleSheet } from 'react-native'
4 |
5 | export function StylesProvider({ children }: { children: React.ReactNode }) {
6 | useServerInsertedHTML(() => {
7 | // @ts-ignore
8 | const sheet = StyleSheet.getSheet()
9 | return (
10 |
14 | )
15 | })
16 | return <>{children}>
17 | }
18 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/app/users/[userId]/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { Text, View } from 'react-native'
3 | import { useParams, useRouter } from 'solito/navigation'
4 |
5 | const useUserParams = useParams<{ userId: string }>
6 |
7 | export default function Home() {
8 | const { userId } = useUserParams()
9 | const router = useRouter()
10 |
11 | return (
12 |
13 | router.back()}>
14 | Hi {userId}, click me to go back
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "app": "*",
13 | "next": "^15.2.3",
14 | "raf": "^3.4.1",
15 | "react": "^19.0.0",
16 | "react-dom": "^19.0.0",
17 | "react-native-web": "~0.19.10"
18 | },
19 | "devDependencies": {
20 | "@types/node": "17.0.21",
21 | "eslint-config-next": "13.2.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example-monorepos/blank/apps/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "baseUrl": ".",
23 | "paths": {
24 | "app/*": ["../../packages/app/*"]
25 | }
26 | },
27 | "include": [
28 | "next-env.d.ts",
29 | "app-env.d.ts",
30 | "**/*.ts",
31 | "**/*.tsx",
32 | "next.config.js",
33 | ".next/types/**/*.ts"
34 | ],
35 | "exclude": ["node_modules"]
36 | }
37 |
--------------------------------------------------------------------------------
/example-monorepos/blank/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solito-blank",
3 | "private": true,
4 | "workspaces": [
5 | "apps/*",
6 | "packages/*"
7 | ],
8 | "devDependencies": {
9 | "@types/react": "^18.2.21",
10 | "eslint": "^8.21.0",
11 | "turbo": "^2.4.4",
12 | "typescript": "^5.2.2"
13 | },
14 | "scripts": {
15 | "native": "cd apps/expo && yarn start",
16 | "web": "cd apps/next && yarn next"
17 | },
18 | "packageManager": "yarn@4.7.0",
19 | "eslintConfig": {
20 | "extends": "next",
21 | "settings": {
22 | "next": {
23 | "rootDir": "apps/next/"
24 | }
25 | },
26 | "root": true
27 | },
28 | "prettier": {
29 | "semi": false,
30 | "useTabs": false,
31 | "tabWidth": 2,
32 | "singleQuote": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/features/user/detail-screen.tsx:
--------------------------------------------------------------------------------
1 | import { View, Text, Pressable } from 'react-native'
2 | import { useRouter } from 'solito/navigation'
3 |
4 | export function UserDetailScreen() {
5 | const router = useRouter()
6 | return (
7 |
8 | router.back()}>
9 | 👈 Go Home
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/index.ts:
--------------------------------------------------------------------------------
1 | // leave this blank
2 | // don't re-export files from this workspace. it'll break next.js tree shaking
3 | // https://github.com/vercel/next.js/issues/12557
4 | export {}
5 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/navigation/native/index.tsx:
--------------------------------------------------------------------------------
1 | import { createNativeStackNavigator } from '@react-navigation/native-stack'
2 |
3 | import { HomeScreen } from 'app/features/home/screen'
4 | import { UserDetailScreen } from 'app/features/user/detail-screen'
5 |
6 | const Stack = createNativeStackNavigator<{
7 | home: undefined
8 | 'user-detail': {
9 | id: string
10 | }
11 | }>()
12 |
13 | export function NativeNavigation() {
14 | return (
15 |
16 |
23 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "name": "app",
4 | "main": "index.ts",
5 | "dependencies": {
6 | "@react-navigation/native": "^7.0.15",
7 | "@react-navigation/native-stack": "^7.2.1",
8 | "dripsy": "^4.3.3",
9 | "moti": "^0.30.0",
10 | "solito": "4.4.1"
11 | },
12 | "sideEffects": false
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/index.tsx:
--------------------------------------------------------------------------------
1 | import { SafeArea } from 'app/provider/safe-area'
2 | import { NavigationProvider } from './navigation'
3 |
4 | export function Provider({ children }: { children: React.ReactNode }) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/navigation/index.native.tsx:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native'
2 | import * as Linking from 'expo-linking'
3 | import { useMemo } from 'react'
4 |
5 | export function NavigationProvider({
6 | children,
7 | }: {
8 | children: React.ReactNode
9 | }) {
10 | return (
11 | ({
14 | prefixes: [Linking.createURL('/')],
15 | config: {
16 | initialRouteName: 'home',
17 | screens: {
18 | home: '',
19 | 'user-detail': 'users/:id',
20 | },
21 | },
22 | }),
23 | []
24 | )}
25 | >
26 | {children}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/navigation/index.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we avoid the provider altogether
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | export const NavigationProvider = ({
6 | children,
7 | }: {
8 | children: React.ReactElement
9 | }) => <>{children}>
10 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/safe-area/index.native.tsx:
--------------------------------------------------------------------------------
1 | export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context'
2 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/safe-area/index.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we are going to avoid the safe area provider
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | // if you need safe area hooks yourself, you can implement this yourself
6 | // however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web
7 |
8 | // for more, see the `./use-safe-area.web.ts` file
9 |
10 | export const SafeArea = ({ children }: { children: React.ReactElement }) => (
11 | <>{children}>
12 | )
13 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/safe-area/use-safe-area.native.ts:
--------------------------------------------------------------------------------
1 | import { useSafeAreaInsets } from 'react-native-safe-area-context'
2 |
3 | const useSafeArea = useSafeAreaInsets
4 |
5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead
6 | export { useSafeArea }
7 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/provider/safe-area/use-safe-area.ts:
--------------------------------------------------------------------------------
1 | // I don't use the real useSafeAreaInsets() hook, since
2 | // 1) the SafeAreaProvider forces you to render null on Web until it measures
3 | // 2) you might not need to support it, unless you're doing landscape stuff
4 | // 3) react-native-safe-area-context has a massive import on Web
5 | // see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313
6 | // 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead
7 | // after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it,
8 | // that is probably better than a massive bundle size for little benefit
9 |
10 | import type { useSafeArea as nativeHook } from './use-safe-area.native'
11 |
12 | const area = {
13 | bottom: 0,
14 | left: 0,
15 | right: 0,
16 | top: 0,
17 |
18 | // you could also use CSS env variables like below:
19 | // but you'll have to be sure to override the types for `useSafeArea`
20 | // and make sure to never add numbers and strings when you consue useSafeArea
21 | // just keep in mind that the env() doesn't work on older browsers I think
22 |
23 | // top: `env(safe-area-inset-top)`,
24 | // right: `env(safe-area-inset-right)`,
25 | // bottom: `env(safe-area-inset-bottom)`,
26 | // left: `env(safe-area-inset-left)`,
27 | }
28 |
29 | export function useSafeArea(): ReturnType {
30 | return area
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/blank/packages/app/rnw-overrides.d.ts:
--------------------------------------------------------------------------------
1 | // override react-native types with react-native-web types
2 | import 'react-native'
3 |
4 | declare module 'react-native' {
5 | interface PressableStateCallbackType {
6 | hovered?: boolean
7 | focused?: boolean
8 | }
9 | interface ViewStyle {
10 | transitionProperty?: string
11 | transitionDuration?: string
12 | }
13 | interface TextProps {
14 | accessibilityComponentType?: never
15 | accessibilityTraits?: never
16 | href?: string
17 | hrefAttrs?: {
18 | rel: 'noreferrer'
19 | target?: '_blank'
20 | }
21 | }
22 | interface ViewProps {
23 | accessibilityRole?: string
24 | href?: string
25 | hrefAttrs?: {
26 | rel: 'noreferrer'
27 | target?: '_blank'
28 | }
29 | onClick?: (e: React.MouseEvent) => void
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/blank/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "noUncheckedIndexedAccess": true,
5 | "paths": {
6 | "app/*": ["./packages/app/*"]
7 | },
8 | "baseUrl": ".",
9 | "jsx": "react-jsx"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/example-monorepos/blank/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turbo.build/schema.json",
3 |
4 | "tasks": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "outputs": [".next/**", "!.next/cache/**"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'next',
3 | settings: {
4 | next: {
5 | rootDir: 'apps/next/',
6 | },
7 | },
8 | root: true,
9 | }
10 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | */fastlane/report.xml
52 | */fastlane/Preview.html
53 | */fastlane/screenshots
54 |
55 | # Bundle artifacts
56 | *.jsbundle
57 |
58 | # CocoaPods
59 | /ios/Pods/
60 |
61 | # Expo
62 | .expo/*
63 | web-build/
64 |
65 | **/*/.expo
66 |
67 | **/*/.next
68 |
69 | **/*/ios
70 | **/*/android
71 |
72 | .turbo
73 | build/**
74 |
75 |
76 | .pnp.*
77 | .yarn/*
78 | !.yarn/patches
79 | !.yarn/plugins
80 | !.yarn/releases
81 | !.yarn/sdks
82 | !.yarn/versions
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "useTabs": false,
4 | "tabWidth": 2,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-3.4.1.cjs
2 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/App.tsx:
--------------------------------------------------------------------------------
1 | import { NativeNavigation } from 'app/navigation/native'
2 | import { Provider } from 'app/provider'
3 | import { Font } from './Fonts'
4 |
5 | export default function App() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/Fonts.tsx:
--------------------------------------------------------------------------------
1 | import { useFonts } from 'expo-font'
2 |
3 | export function Font({ children }) {
4 | const [ready] = useFonts({
5 | ['Inter-Regular']: require('../next/public/font/Inter/Inter-Regular.otf'),
6 | ['Inter-Bold']: require('../next/public/font/Inter/Inter-Bold.otf'),
7 | ['Inter-Black']: require('../next/public/font/Inter/Inter-Black.otf'),
8 | })
9 |
10 | if (!ready) {
11 | return null
12 | }
13 |
14 | return <>{children}>
15 | }
16 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "solito-blank",
4 | "slug": "solito-blank",
5 | "version": "1.0.0",
6 | "scheme": "solito-with-font",
7 | "platforms": ["ios", "android"],
8 | "ios": {
9 | "bundleIdentifier": "com.solito.with-font"
10 | },
11 | "assetBundlePatterns": ["**/*", "../next/public/font/**/*"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 | return {
4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]],
5 | plugins: ['react-native-reanimated/plugin'],
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo';
2 |
3 | import App from './App';
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in Expo Go or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App);
9 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.dev/guides/monorepos
2 | // Learn more https://docs.expo.io/guides/customizing-metro
3 | /**
4 | * @type {import('expo/metro-config')}
5 | */
6 | const { getDefaultConfig } = require('expo/metro-config')
7 | const path = require('path')
8 |
9 | // Find the project and workspace directories
10 | const projectRoot = __dirname
11 | // This can be replaced with `find-yarn-workspace-root`
12 | const workspaceRoot = path.resolve(projectRoot, '../..')
13 |
14 | const config = getDefaultConfig(projectRoot)
15 |
16 | // 1. Watch all files within the monorepo
17 | config.watchFolders = [workspaceRoot]
18 | // 2. Let Metro know where to resolve packages and in what order
19 | config.resolver.nodeModulesPaths = [
20 | path.resolve(projectRoot, 'node_modules'),
21 | path.resolve(workspaceRoot, 'node_modules'),
22 | ]
23 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
24 | config.resolver.disableHierarchicalLookup = true
25 |
26 | module.exports = config
27 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "app": "*",
4 | "expo": "^49.0.0",
5 | "expo-font": "~11.4.0",
6 | "expo-linear-gradient": "~12.3.0",
7 | "expo-linking": "~5.0.2",
8 | "expo-splash-screen": "~0.20.5",
9 | "expo-status-bar": "~1.6.0",
10 | "react": "18.2.0",
11 | "react-dom": "18.2.0",
12 | "react-native": "0.72.4",
13 | "react-native-gesture-handler": "~2.12.0",
14 | "react-native-reanimated": "~3.3.0",
15 | "react-native-safe-area-context": "4.6.3",
16 | "react-native-screens": "~3.22.0",
17 | "react-native-web": "~0.19.6"
18 | },
19 | "devDependencies": {
20 | "@babel/core": "^7.20.0",
21 | "@types/react": "~18.2.14",
22 | "@types/react-native": "~0.72.2",
23 | "typescript": "^5.2.2"
24 | },
25 | "scripts": {
26 | "start": "expo start",
27 | "android": "expo run:android",
28 | "ios": "expo run:ios",
29 | "web": "expo start --web"
30 | },
31 | "main": "index.js",
32 | "version": "1.0.0",
33 | "private": true,
34 | "name": "expo-app"
35 | }
36 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/next.config.js:
--------------------------------------------------------------------------------
1 | const { withExpo } = require('@expo/next-adapter')
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | // reanimated (and thus, Moti) doesn't work with strict mode currently...
6 | // https://github.com/nandorojo/moti/issues/224
7 | // https://github.com/necolas/react-native-web/pull/2330
8 | // https://github.com/nandorojo/moti/issues/224
9 | // once that gets fixed, set this back to true
10 | reactStrictMode: false,
11 | transpilePackages: [
12 | 'react-native',
13 | 'react-native-web',
14 | 'solito',
15 | 'dripsy',
16 | '@dripsy/core',
17 | 'moti',
18 | 'app',
19 | 'react-native-reanimated',
20 | '@expo/html-elements',
21 | 'react-native-gesture-handler',
22 | ],
23 | }
24 |
25 | module.exports = withExpo(nextConfig)
26 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@expo/next-adapter": "5.0.2",
13 | "app": "*",
14 | "next": "13.4.19",
15 | "raf": "^3.4.1",
16 | "setimmediate": "^1.0.5"
17 | },
18 | "devDependencies": {
19 | "@types/node": "17.0.21",
20 | "eslint-config-next": "13.2.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import 'raf/polyfill'
2 | import 'setimmediate'
3 |
4 | import { Provider } from 'app/provider'
5 | import Head from 'next/head'
6 | import React from 'react'
7 | import type { SolitoAppProps } from 'solito'
8 |
9 | function MyApp({ Component, pageProps }: SolitoAppProps) {
10 | return (
11 | <>
12 |
13 | Solito Fonts
14 |
18 |
19 |
20 |
21 |
22 |
23 | >
24 | )
25 | }
26 |
27 | export default MyApp
28 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { HomeScreen } from 'app/features/home/screen'
2 |
3 | export default HomeScreen
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/pages/user/[id].tsx:
--------------------------------------------------------------------------------
1 | import { UserDetailScreen } from 'app/features/user/detail-screen'
2 |
3 | export default UserDetailScreen
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/favicon.ico
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Black.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Black.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-BlackItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-BlackItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Bold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Bold.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-BoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-BoldItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraBold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraBold.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraBoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraBoldItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraLight.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraLight.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraLightItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ExtraLightItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Italic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Italic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Light.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-LightItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-LightItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Medium.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Medium.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-MediumItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-MediumItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Regular.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-SemiBold.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-SemiBold.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-SemiBoldItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-SemiBoldItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Thin.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-Thin.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ThinItalic.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-ThinItalic.otf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-V.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-custom-font/apps/next/public/font/Inter/Inter-V.ttf
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/apps/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "app-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solito-blank",
3 | "private": true,
4 | "workspaces": [
5 | "apps/*",
6 | "packages/*"
7 | ],
8 | "devDependencies": {
9 | "@types/react": "^18.2.21",
10 | "@types/react-native": "^0.72.2",
11 | "eslint": "^8.21.0",
12 | "turbo": "^1.4.2",
13 | "typescript": "^5.2.2"
14 | },
15 | "scripts": {
16 | "native": "cd apps/expo && yarn start",
17 | "web": "cd apps/next && yarn next"
18 | },
19 | "packageManager": "yarn@3.4.1"
20 | }
21 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/features/user/detail-screen.tsx:
--------------------------------------------------------------------------------
1 | import { View, Text } from 'dripsy'
2 | import { createParam } from 'solito'
3 | import { TextLink } from 'solito/link'
4 |
5 | const { useParam } = createParam<{ id: string }>()
6 |
7 | export function UserDetailScreen() {
8 | const [id] = useParam('id')
9 |
10 | return (
11 |
12 | {`User ID: ${id}`}
15 |
16 | 👈 Go Home
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/index.ts:
--------------------------------------------------------------------------------
1 | // leave this blank
2 | // don't re-export files from this workspace. it'll break next.js tree shaking
3 | // https://github.com/vercel/next.js/issues/12557
4 | export {}
5 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/navigation/native/index.tsx:
--------------------------------------------------------------------------------
1 | import { createNativeStackNavigator } from '@react-navigation/native-stack'
2 |
3 | import { HomeScreen } from '../../features/home/screen'
4 | import { UserDetailScreen } from '../../features/user/detail-screen'
5 |
6 | const Stack = createNativeStackNavigator<{
7 | home: undefined
8 | 'user-detail': {
9 | id: string
10 | }
11 | }>()
12 |
13 | export function NativeNavigation() {
14 | return (
15 |
16 |
23 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "name": "app",
4 | "main": "index.ts",
5 | "dependencies": {
6 | "@react-navigation/native": "7.0.0",
7 | "@react-navigation/native-stack": "7.0.0",
8 | "dripsy": "^4.3.3",
9 | "moti": "latest",
10 | "solito": "4.3.0"
11 | },
12 | "sideEffects": false
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/dripsy.tsx:
--------------------------------------------------------------------------------
1 | import { DripsyProvider, makeTheme } from 'dripsy'
2 | import { Platform } from 'react-native'
3 |
4 | const rootFontName = 'Inter-Regular'
5 |
6 | const webFont = (font: string) =>
7 | Platform.select({
8 | web: `${font}, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, Inter-serif`,
9 | default: font,
10 | })
11 |
12 | const theme = makeTheme({
13 | // https://www.dripsy.xyz/usage/theming/create
14 | text: {
15 | p: {
16 | fontSize: 16,
17 | },
18 | },
19 | customFonts: {
20 | [rootFontName]: {
21 | '400': webFont(rootFontName),
22 | '500': webFont(rootFontName),
23 | 600: webFont('Inter-Bold'),
24 | 700: webFont('Inter-Bold'),
25 | 800: webFont('Inter-Black'),
26 | 900: webFont('Inter-Black'),
27 | bold: webFont('Inter-Bold'),
28 | default: webFont(rootFontName),
29 | normal: webFont(rootFontName),
30 | },
31 | },
32 | fonts: {
33 | root: rootFontName,
34 | },
35 | })
36 |
37 | export function Dripsy({ children }: { children: React.ReactNode }) {
38 | return (
39 |
44 | {children}
45 |
46 | )
47 | }
48 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/index.tsx:
--------------------------------------------------------------------------------
1 | import { Dripsy } from './dripsy'
2 | import { NavigationProvider } from './navigation'
3 |
4 | export function Provider({ children }: { children: React.ReactNode }) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/navigation/index.tsx:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native'
2 | import * as Linking from 'expo-linking'
3 | import { useMemo } from 'react'
4 |
5 | export function NavigationProvider({
6 | children,
7 | }: {
8 | children: React.ReactNode
9 | }) {
10 | return (
11 | ({
14 | prefixes: [Linking.createURL('/')],
15 | config: {
16 | initialRouteName: 'home',
17 | screens: {
18 | home: '',
19 | 'user-detail': 'user/:id',
20 | },
21 | },
22 | }),
23 | []
24 | )}
25 | >
26 | {children}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/navigation/index.web.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we avoid the provider altogether
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | export const NavigationProvider = ({
6 | children,
7 | }: {
8 | children: React.ReactElement
9 | }) => <>{children}>
10 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/safe-area/index.tsx:
--------------------------------------------------------------------------------
1 | export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context'
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/safe-area/index.web.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we are going to avoid the safe area provider
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | // if you need safe area hooks yourself, you can implement this yourself
6 | // however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web
7 |
8 | // for more, see the `./use-safe-area.web.ts` file
9 |
10 | export const SafeArea = ({ children }: { children: React.ReactElement }) => (
11 | <>{children}>
12 | )
13 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/safe-area/use-safe-area.ts:
--------------------------------------------------------------------------------
1 | import { useSafeAreaInsets } from 'react-native-safe-area-context'
2 |
3 | const useSafeArea = useSafeAreaInsets
4 |
5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead
6 | export { useSafeArea }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/provider/safe-area/use-safe-area.web.ts:
--------------------------------------------------------------------------------
1 | // I don't use the real useSafeAreaInsets() hook, since
2 | // 1) the SafeAreaProvider forces you to render null on Web until it measures
3 | // 2) you might not need to support it, unless you're doing landscape stuff
4 | // 3) react-native-safe-area-context has a massive import on Web
5 | // see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313
6 | // 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead
7 | // after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it,
8 | // that is probably better than a massive bundle size for little benefit
9 |
10 | import type { useSafeArea as nativeHook } from './use-safe-area'
11 |
12 | const area = {
13 | bottom: 0,
14 | left: 0,
15 | right: 0,
16 | top: 0,
17 |
18 | // you could also use CSS env variables like below:
19 | // but you'll have to be sure to override the types for `useSafeArea`
20 | // and make sure to never add numbers and strings when you consue useSafeArea
21 | // just keep in mind that the env() doesn't work on older browsers I think
22 |
23 | // top: `env(safe-area-inset-top)`,
24 | // right: `env(safe-area-inset-right)`,
25 | // bottom: `env(safe-area-inset-bottom)`,
26 | // left: `env(safe-area-inset-left)`,
27 | }
28 |
29 | export function useSafeArea(): ReturnType {
30 | return area
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/packages/app/rnw-overrides.d.ts:
--------------------------------------------------------------------------------
1 | // override react-native types with react-native-web types
2 | import 'react-native'
3 |
4 | declare module 'react-native' {
5 | interface PressableStateCallbackType {
6 | hovered?: boolean
7 | focused?: boolean
8 | }
9 | interface ViewStyle {
10 | transitionProperty?: string
11 | transitionDuration?: string
12 | }
13 | interface TextProps {
14 | accessibilityComponentType?: never
15 | accessibilityTraits?: never
16 | href?: string
17 | hrefAttrs?: {
18 | rel: 'noreferrer'
19 | target?: '_blank'
20 | }
21 | }
22 | interface ViewProps {
23 | accessibilityRole?: string
24 | href?: string
25 | hrefAttrs?: {
26 | rel: 'noreferrer'
27 | target?: '_blank'
28 | }
29 | onClick?: (e: React.MouseEvent) => void
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "noUncheckedIndexedAccess": true,
5 | "paths": {
6 | "app/*": ["./packages/app/*"]
7 | }
8 | },
9 | "extends": "expo/tsconfig.base"
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-custom-font/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pipeline": {
3 | "build": {
4 | "dependsOn": ["^build"],
5 | "outputs": [".next/**"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'next',
3 | settings: {
4 | next: {
5 | rootDir: 'apps/next/',
6 | },
7 | },
8 | root: true,
9 | }
10 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | */fastlane/report.xml
52 | */fastlane/Preview.html
53 | */fastlane/screenshots
54 |
55 | # Bundle artifacts
56 | *.jsbundle
57 |
58 | # CocoaPods
59 | /ios/Pods/
60 |
61 | # Expo
62 | .expo/*
63 | web-build/
64 |
65 | **/*/.expo
66 |
67 | **/*/.next
68 |
69 | **/*/ios
70 | **/*/android
71 |
72 | .turbo
73 | build/**
74 |
75 |
76 | .pnp.*
77 | .yarn/*
78 | !.yarn/patches
79 | !.yarn/plugins
80 | !.yarn/releases
81 | !.yarn/sdks
82 | !.yarn/versions
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "useTabs": false,
4 | "tabWidth": 2,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-3.4.1.cjs
2 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "solito-blank",
4 | "slug": "solito-blank",
5 | "version": "1.0.0",
6 | "scheme": "solito-blank",
7 | "platforms": ["ios", "android"],
8 | "ios": {
9 | "bundleIdentifier": "com.solito.blank"
10 | },
11 | "newArchEnabled": true,
12 | "experiments": {
13 | "reactCanary": true,
14 | "reactCompiler": true
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/app/_layout.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from 'app/provider'
2 | import { Stack } from 'expo-router'
3 |
4 | export default function Root() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/app/index.tsx:
--------------------------------------------------------------------------------
1 | import { HomeScreen } from 'app/features/home/screen'
2 |
3 | export default function Home() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/app/user/[id].tsx:
--------------------------------------------------------------------------------
1 | import { UserDetailScreen } from 'app/features/user/detail-screen'
2 |
3 | export default function UserDetail() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 | return {
4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]],
5 | plugins: ['react-native-reanimated/plugin'],
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.dev/guides/monorepos
2 | // Learn more https://docs.expo.io/guides/customizing-metro
3 | /**
4 | * @type {import('expo/metro-config')}
5 | */
6 | const { getDefaultConfig } = require('expo/metro-config')
7 | const path = require('path')
8 |
9 | const projectRoot = __dirname
10 | const workspaceRoot = path.resolve(projectRoot, '../..')
11 |
12 | const config = getDefaultConfig(projectRoot)
13 |
14 | config.watchFolders = [workspaceRoot]
15 | config.resolver.nodeModulesPaths = [
16 | path.resolve(projectRoot, 'node_modules'),
17 | path.resolve(workspaceRoot, 'node_modules'),
18 | ]
19 | config.resolver.disableHierarchicalLookup = true
20 |
21 | config.transformer.getTransformOptions = async () => ({
22 | transform: {
23 | experimentalImportSupport: false,
24 | inlineRequires: true,
25 | },
26 | })
27 |
28 | module.exports = config
29 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "app": "*",
4 | "expo": "52.0.38",
5 | "expo-image": "~2.0.6",
6 | "expo-linear-gradient": "~14.0.2",
7 | "expo-linking": "~7.0.5",
8 | "expo-router": "^4.0.19",
9 | "expo-splash-screen": "~0.29.22",
10 | "expo-status-bar": "~2.0.1",
11 | "moti": "^0.30.0",
12 | "react-native": "0.76.7",
13 | "react-native-gesture-handler": "~2.20.2",
14 | "react-native-reanimated": "3.16.1",
15 | "react-native-safe-area-context": "4.12.0",
16 | "react-native-screens": "~4.4.0"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.24.0",
20 | "@types/react": "~18.3.12",
21 | "babel-plugin-react-compiler": "19.0.0-beta-3229e95-20250315",
22 | "react-compiler-runtime": "19.0.0-beta-3229e95-20250315",
23 | "typescript": "~5.3.3"
24 | },
25 | "scripts": {
26 | "start": "expo start",
27 | "android": "expo run:android",
28 | "ios": "expo run:ios"
29 | },
30 | "main": "expo-router/entry",
31 | "version": "1.0.0",
32 | "private": true,
33 | "name": "expo-app",
34 | "expo": {
35 | "install": {
36 | "exclude": [
37 | "react"
38 | ]
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/api/hello/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET(request: Request) {
2 | return new Response('Hello, Solito!')
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-expo-router/apps/next/app/favicon.ico
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body,
3 | #__next {
4 | width: 100%;
5 | /* To smooth any scrolling behavior */
6 | -webkit-overflow-scrolling: touch;
7 | margin: 0px;
8 | padding: 0px;
9 | /* Allows content to fill the viewport and go beyond the bottom */
10 | min-height: 100%;
11 | }
12 | #__next {
13 | flex-shrink: 0;
14 | flex-basis: auto;
15 | flex-direction: column;
16 | flex-grow: 1;
17 | display: flex;
18 | flex: 1;
19 | }
20 | html {
21 | scroll-behavior: smooth;
22 | /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
23 | -webkit-text-size-adjust: 100%;
24 | height: 100%;
25 | }
26 | body {
27 | display: flex;
28 | /* Allows you to scroll below the viewport; default value is visible */
29 | overflow-y: auto;
30 | overscroll-behavior-y: none;
31 | text-rendering: optimizeLegibility;
32 | -webkit-font-smoothing: antialiased;
33 | -moz-osx-font-smoothing: grayscale;
34 | -ms-overflow-style: scrollbar;
35 | }
36 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import { StylesProvider } from './styles-provider'
2 | import './globals.css'
3 |
4 | export const metadata = {
5 | title: 'Create Solito App',
6 | description: 'Generated by create Solito app',
7 | }
8 |
9 | export default function RootLayout({
10 | children,
11 | }: {
12 | children: React.ReactNode
13 | }) {
14 | return (
15 |
16 |
17 | {children}
18 |
19 |
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | export { HomeScreen as default } from 'app/features/home/screen'
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/styles-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { useServerInsertedHTML } from 'next/navigation'
3 | import { StyleSheet } from 'react-native'
4 |
5 | export function StylesProvider({ children }: { children: React.ReactNode }) {
6 | useServerInsertedHTML(() => {
7 | // @ts-ignore
8 | const sheet = StyleSheet.getSheet()
9 | return (
10 |
14 | )
15 | })
16 | return <>{children}>
17 | }
18 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/app/users/[userId]/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { Text, View } from 'react-native'
3 | import { useParams, useRouter } from 'solito/navigation'
4 |
5 | const useUserParams = useParams<{ userId: string }>
6 |
7 | export default function Home() {
8 | const { userId } = useUserParams()
9 | const router = useRouter()
10 |
11 | return (
12 |
13 | router.back()}>
14 | Hi {userId}, click me to go back
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "app": "*",
13 | "next": "latest",
14 | "raf": "^3.4.1",
15 | "react": "19.0.0-rc.1",
16 | "react-dom": "19.0.0-rc.1",
17 | "react-native-gesture-handler": "2.20.2",
18 | "react-native-reanimated": "3.16.1",
19 | "react-native-web": "^0.19.13"
20 | },
21 | "devDependencies": {
22 | "@types/node": "17.0.21",
23 | "eslint-config-next": "13.2.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/plugins/swc_plugin_reanimated.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-expo-router/apps/next/plugins/swc_plugin_reanimated.wasm
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-expo-router/apps/next/public/favicon.ico
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/apps/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": true,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve",
20 | "incremental": true,
21 | "plugins": [
22 | {
23 | "name": "next"
24 | }
25 | ]
26 | },
27 | "include": [
28 | "**/*.ts",
29 | "**/*.tsx",
30 | "app-env.d.ts",
31 | "next-env.d.ts",
32 | ".next/types/**/*.ts"
33 | ],
34 | "exclude": [
35 | "node_modules"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solito-expo-router",
3 | "private": true,
4 | "workspaces": [
5 | "apps/*",
6 | "packages/*"
7 | ],
8 | "devDependencies": {
9 | "@types/react": "~18.2.21",
10 | "@types/react-native": "^0.72.2",
11 | "eslint": "^8.21.0",
12 | "turbo": "^1.4.2",
13 | "typescript": "^5.2.2"
14 | },
15 | "scripts": {
16 | "native": "cd apps/expo && yarn start",
17 | "web": "cd apps/next && yarn next"
18 | },
19 | "resolutions": {
20 | "metro": "~0.76.7",
21 | "metro-resolver": "~0.76.7"
22 | },
23 | "nohoist": [
24 | "**/expo-router",
25 | "**/expo-router/**"
26 | ],
27 | "packageManager": "yarn@3.4.1"
28 | }
29 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/features/user/detail-screen.tsx:
--------------------------------------------------------------------------------
1 | import { View, Text, Pressable } from 'react-native'
2 | import { useRouter } from 'solito/navigation'
3 |
4 | export function UserDetailScreen() {
5 | const router = useRouter()
6 | return (
7 |
8 | router.back()}>
9 | 👈 Go Home
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/index.ts:
--------------------------------------------------------------------------------
1 | // leave this blank
2 | // don't re-export files from this workspace. it'll break next.js tree shaking
3 | // https://github.com/vercel/next.js/issues/12557
4 | export {}
5 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/navigation/native/index.tsx:
--------------------------------------------------------------------------------
1 | import { createNativeStackNavigator } from '@react-navigation/native-stack'
2 |
3 | // import { HomeScreen } from 'app/features/home/screen'
4 | import { UserDetailScreen } from 'app/features/user/detail-screen'
5 | import { Text, View } from 'react-native'
6 | import { Link as MotiLink } from 'solito/link'
7 | import { MotiView } from 'moti'
8 |
9 | const Stack = createNativeStackNavigator<{
10 | home: undefined
11 | 'user-detail': {
12 | id: string
13 | }
14 | }>()
15 |
16 | const HomeScreen = () => {
17 | return (
18 |
19 | Home
20 |
21 |
22 | Go to user
23 |
24 |
25 |
26 | )
27 | }
28 |
29 | export function NativeNavigation() {
30 | return (
31 |
32 |
39 |
46 |
47 | )
48 | }
49 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "name": "app",
4 | "main": "index.ts",
5 | "dependencies": {
6 | "@react-navigation/native": "^7.0.15",
7 | "@react-navigation/native-stack": "^7.2.1",
8 | "dripsy": "^4.3.3",
9 | "moti": "^0.30.0",
10 | "solito": "4.4.1"
11 | },
12 | "sideEffects": false
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/index.tsx:
--------------------------------------------------------------------------------
1 | import { SafeArea } from 'app/provider/safe-area'
2 | import { NavigationProvider } from './navigation'
3 |
4 | export function Provider({ children }: { children: React.ReactNode }) {
5 | return (
6 |
7 | {children}
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/navigation/index.native.tsx:
--------------------------------------------------------------------------------
1 | import { NavigationContainer } from '@react-navigation/native'
2 | import * as Linking from 'expo-linking'
3 | import { useMemo } from 'react'
4 |
5 | export function NavigationProvider({
6 | children,
7 | }: {
8 | children: React.ReactNode
9 | }) {
10 | return (
11 | ({
14 | prefixes: [Linking.createURL('/')],
15 | config: {
16 | initialRouteName: 'home',
17 | screens: {
18 | home: '',
19 | 'user-detail': 'user/:id',
20 | },
21 | },
22 | }),
23 | []
24 | )}
25 | >
26 | {children}
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/navigation/index.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we avoid the provider altogether
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | export const NavigationProvider = ({
6 | children,
7 | }: {
8 | children: React.ReactElement
9 | }) => <>{children}>
10 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/safe-area/index.native.tsx:
--------------------------------------------------------------------------------
1 | export { SafeAreaProvider as SafeArea } from 'react-native-safe-area-context'
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/safe-area/index.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we are going to avoid the safe area provider
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | // if you need safe area hooks yourself, you can implement this yourself
6 | // however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web
7 |
8 | // for more, see the `./use-safe-area.web.ts` file
9 |
10 | export const SafeArea = ({ children }: { children: React.ReactElement }) => (
11 | <>{children}>
12 | )
13 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/safe-area/use-safe-area.native.ts:
--------------------------------------------------------------------------------
1 | import { useSafeAreaInsets } from 'react-native-safe-area-context'
2 |
3 | const useSafeArea = useSafeAreaInsets
4 |
5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead
6 | export { useSafeArea }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/provider/safe-area/use-safe-area.ts:
--------------------------------------------------------------------------------
1 | // I don't use the real useSafeAreaInsets() hook, since
2 | // 1) the SafeAreaProvider forces you to render null on Web until it measures
3 | // 2) you might not need to support it, unless you're doing landscape stuff
4 | // 3) react-native-safe-area-context has a massive import on Web
5 | // see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313
6 | // 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead
7 | // after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it,
8 | // that is probably better than a massive bundle size for little benefit
9 |
10 | import type { useSafeArea as nativeHook } from './use-safe-area.native'
11 |
12 | const area = {
13 | bottom: 0,
14 | left: 0,
15 | right: 0,
16 | top: 0,
17 |
18 | // you could also use CSS env variables like below:
19 | // but you'll have to be sure to override the types for `useSafeArea`
20 | // and make sure to never add numbers and strings when you consue useSafeArea
21 | // just keep in mind that the env() doesn't work on older browsers I think
22 |
23 | // top: `env(safe-area-inset-top)`,
24 | // right: `env(safe-area-inset-right)`,
25 | // bottom: `env(safe-area-inset-bottom)`,
26 | // left: `env(safe-area-inset-left)`,
27 | }
28 |
29 | export function useSafeArea(): ReturnType {
30 | return area
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/packages/app/rnw-overrides.d.ts:
--------------------------------------------------------------------------------
1 | // override react-native types with react-native-web types
2 | import 'react-native'
3 |
4 | declare module 'react-native' {
5 | interface PressableStateCallbackType {
6 | hovered?: boolean
7 | focused?: boolean
8 | }
9 | interface ViewStyle {
10 | transitionProperty?: string
11 | transitionDuration?: string
12 | }
13 | interface TextProps {
14 | accessibilityComponentType?: never
15 | accessibilityTraits?: never
16 | href?: string
17 | hrefAttrs?: {
18 | rel: 'noreferrer'
19 | target?: '_blank'
20 | }
21 | }
22 | interface ViewProps {
23 | accessibilityRole?: string
24 | href?: string
25 | hrefAttrs?: {
26 | rel: 'noreferrer'
27 | target?: '_blank'
28 | }
29 | onClick?: (e: React.MouseEvent) => void
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "noUncheckedIndexedAccess": true,
5 | "paths": {
6 | "app/*": ["./packages/app/*"]
7 | },
8 | "jsx": "react-jsx"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-expo-router/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pipeline": {
3 | "build": {
4 | "dependsOn": ["^build"],
5 | "outputs": [".next/**"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'next',
3 | settings: {
4 | next: {
5 | rootDir: 'apps/next/',
6 | },
7 | },
8 | root: true,
9 | }
10 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 |
33 | # node.js
34 | #
35 | node_modules/
36 | npm-debug.log
37 | yarn-error.log
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 | # fastlane
45 | #
46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
47 | # screenshots whenever they are needed.
48 | # For more information about the recommended setup visit:
49 | # https://docs.fastlane.tools/best-practices/source-control/
50 |
51 | */fastlane/report.xml
52 | */fastlane/Preview.html
53 | */fastlane/screenshots
54 |
55 | # Bundle artifacts
56 | *.jsbundle
57 |
58 | # CocoaPods
59 | /ios/Pods/
60 |
61 | # Expo
62 | .expo/*
63 | web-build/
64 |
65 | **/*/.expo
66 |
67 | **/*/.next
68 |
69 | **/*/ios
70 | **/*/android
71 |
72 | .turbo
73 | build/**
74 |
75 |
76 | .pnp.*
77 | .yarn/*
78 | !.yarn/patches
79 | !.yarn/plugins
80 | !.yarn/releases
81 | !.yarn/sdks
82 | !.yarn/versions
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "useTabs": false,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "plugins": ["prettier-plugin-tailwindcss"]
7 | }
8 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/.yarnrc.yml:
--------------------------------------------------------------------------------
1 | yarnPath: .yarn/releases/yarn-3.4.1.cjs
2 | nodeLinker: node-modules
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "solito-nativewind",
4 | "slug": "solito-nativewind",
5 | "version": "1.0.0",
6 | "scheme": "solito-nativewind",
7 | "platforms": ["ios", "android"],
8 | "ios": {
9 | "bundleIdentifier": "com.solito.nativewind"
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/app/_layout.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from 'app/provider'
2 | import { Stack } from 'expo-router'
3 |
4 | export default function Root() {
5 | return (
6 |
7 |
8 |
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/app/index.tsx:
--------------------------------------------------------------------------------
1 | import { HomeScreen } from 'app/features/home/screen'
2 |
3 | export default function Home() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/app/user/[id].tsx:
--------------------------------------------------------------------------------
1 | import { UserDetailScreen } from 'app/features/user/detail-screen'
2 |
3 | export default function UserDetail() {
4 | return
5 | }
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true)
3 | return {
4 | presets: [['babel-preset-expo', { jsxRuntime: 'automatic' }]],
5 | plugins: [
6 | 'react-native-reanimated/plugin',
7 | 'nativewind/babel',
8 | 'expo-router/babel',
9 | ],
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/index.js:
--------------------------------------------------------------------------------
1 | // registerRootComponent happens in "expo-router/entry"
2 | import 'expo-router/entry'
3 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/metro.config.js:
--------------------------------------------------------------------------------
1 | // Learn more https://docs.expo.dev/guides/monorepos
2 | // Learn more https://docs.expo.io/guides/customizing-metro
3 | /**
4 | * @type {import('expo/metro-config')}
5 | */
6 | const { getDefaultConfig } = require('expo/metro-config')
7 | const path = require('path')
8 |
9 | // Find the project and workspace directories
10 | const projectRoot = __dirname
11 | // This can be replaced with `find-yarn-workspace-root`
12 | const workspaceRoot = path.resolve(projectRoot, '../..')
13 |
14 | const config = getDefaultConfig(projectRoot)
15 |
16 | // 1. Watch all files within the monorepo
17 | config.watchFolders = [workspaceRoot]
18 | // 2. Let Metro know where to resolve packages and in what order
19 | config.resolver.nodeModulesPaths = [
20 | path.resolve(projectRoot, 'node_modules'),
21 | path.resolve(workspaceRoot, 'node_modules'),
22 | ]
23 | // 3. Force Metro to resolve (sub)dependencies only from the `nodeModulesPaths`
24 | config.resolver.disableHierarchicalLookup = true
25 |
26 | module.exports = config
27 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "app": "*",
4 | "expo": "^49.0.0",
5 | "expo-linking": "~5.0.2",
6 | "expo-router": "^2.0.5",
7 | "expo-splash-screen": "~0.20.5",
8 | "expo-status-bar": "~1.6.0",
9 | "react": "18.2.0",
10 | "react-dom": "18.2.0",
11 | "react-native": "0.72.4",
12 | "react-native-gesture-handler": "~2.12.0",
13 | "react-native-reanimated": "~3.3.0",
14 | "react-native-safe-area-context": "4.6.3",
15 | "react-native-screens": "~3.22.0",
16 | "react-native-web": "~0.19.6"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.20.0",
20 | "@types/react": "^18.2.21",
21 | "@types/react-native": "^0.72.2",
22 | "tailwindcss": "^3.0.24",
23 | "typescript": "^5.2.2"
24 | },
25 | "scripts": {
26 | "start": "expo start",
27 | "android": "expo run:android",
28 | "ios": "expo run:ios",
29 | "web": "expo start --web"
30 | },
31 | "main": "index.js",
32 | "version": "1.0.0",
33 | "private": true,
34 | "name": "expo-app"
35 | }
36 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/tailwind.config.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | const { theme } = require('app/design/tailwind/theme')
4 |
5 | /**
6 | * @type {import('tailwindcss').Config}
7 | */
8 | module.exports = {
9 | content: ['./App.tsx', '../../packages/**/*.{js,jsx,ts,tsx}'],
10 | theme: {
11 | ...theme,
12 | },
13 | plugins: [],
14 | }
15 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/expo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 |
37 | # typescript
38 | *.tsbuildinfo
39 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/global.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Do not edit this file!
3 | * You should not write CSS directly when using React Native.
4 | * We are using CSS resets here to support React Native for Web and Tailwind CSS.
5 | */
6 |
7 | @tailwind base;
8 | @tailwind components;
9 | @tailwind utilities;
10 |
11 | /**
12 | * Building on the RNWeb reset:
13 | * https://github.com/necolas/react-native-web/blob/master/packages/react-native-web/src/exports/StyleSheet/initialRules.js
14 | */
15 | html, body, #__next {
16 | width: 100%;
17 | /* To smooth any scrolling behavior */
18 | -webkit-overflow-scrolling: touch;
19 | margin: 0px;
20 | padding: 0px;
21 | /* Allows content to fill the viewport and go beyond the bottom */
22 | min-height: 100%;
23 | }
24 |
25 | #__next {
26 | flex-shrink: 0;
27 | flex-basis: auto;
28 | flex-direction: column;
29 | flex-grow: 1;
30 | display: flex;
31 | flex: 1;
32 | }
33 |
34 | html {
35 | /* Prevent text size change on orientation change https://gist.github.com/tfausak/2222823#file-ios-8-web-app-html-L138 */
36 | -webkit-text-size-adjust: 100%;
37 | height: 100%;
38 | }
39 |
40 | body {
41 | display: flex;
42 | /* Allows you to scroll below the viewport; default value is visible */
43 | overflow-y: auto;
44 | overscroll-behavior-y: none;
45 | text-rendering: optimizeLegibility;
46 | -webkit-font-smoothing: antialiased;
47 | -moz-osx-font-smoothing: grayscale;
48 | -ms-overflow-style: scrollbar;
49 | }
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/basic-features/typescript for more information.
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/next.config.js:
--------------------------------------------------------------------------------
1 | const { withExpo } = require('@expo/next-adapter')
2 |
3 | /** @type {import('next').NextConfig} */
4 | const nextConfig = {
5 | // reanimated (and thus, Moti) doesn't work with strict mode currently...
6 | // https://github.com/nandorojo/moti/issues/224
7 | // https://github.com/necolas/react-native-web/pull/2330
8 | // https://github.com/nandorojo/moti/issues/224
9 | // once that gets fixed, set this back to true
10 | reactStrictMode: false,
11 | transpilePackages: [
12 | 'react-native',
13 | 'react-native-web',
14 | 'solito',
15 | 'moti',
16 | 'app',
17 | 'react-native-reanimated',
18 | 'nativewind',
19 | 'react-native-gesture-handler',
20 | ],
21 | }
22 |
23 | module.exports = withExpo(nextConfig)
24 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@expo/next-adapter": "5.0.2",
13 | "app": "*",
14 | "next": "13.4.19",
15 | "raf": "^3.4.1",
16 | "setimmediate": "^1.0.5"
17 | },
18 | "devDependencies": {
19 | "@types/node": "17.0.21",
20 | "autoprefixer": "^10.4.7",
21 | "eslint-config-next": "13.2.0",
22 | "tailwindcss": "^3.0.24"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import 'raf/polyfill'
2 | import 'setimmediate'
3 |
4 | import { Provider } from 'app/provider'
5 | import Head from 'next/head'
6 | import React from 'react'
7 |
8 | import '../global.css'
9 | import { AppProps } from 'next/app'
10 |
11 | function MyApp({ Component, pageProps }: AppProps) {
12 | return (
13 | <>
14 |
15 | Solito Example App
16 |
20 |
21 |
22 |
23 |
24 |
25 | >
26 | )
27 | }
28 |
29 | export default MyApp
30 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { AppRegistry } from 'react-native'
3 |
4 | import NextDocument, { Html, Head, Main, NextScript } from 'next/document'
5 | import type { DocumentContext } from 'next/document'
6 |
7 | class Document extends NextDocument {
8 | static async getInitialProps(ctx: DocumentContext) {
9 | AppRegistry.registerComponent('Main', () => Main)
10 | // @ts-ignore
11 | const { getStyleElement } = AppRegistry.getApplication('Main')
12 | const styles = [getStyleElement()]
13 |
14 | const initialProps = await NextDocument.getInitialProps(ctx)
15 | return { ...initialProps, styles: React.Children.toArray(styles) }
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 | }
32 | }
33 |
34 | export default Document
35 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { HomeScreen } from 'app/features/home/screen'
2 |
3 | export default HomeScreen
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/pages/user/[id].tsx:
--------------------------------------------------------------------------------
1 | import { UserDetailScreen } from 'app/features/user/detail-screen'
2 |
3 | export default UserDetailScreen
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/plugins/swc_plugin_reanimated.wasm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-tailwind/apps/next/plugins/swc_plugin_reanimated.wasm
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nandorojo/solito/be03583de99426d9ac8f8504e9ef90924e79b264/example-monorepos/with-tailwind/apps/next/public/favicon.ico
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const { theme } = require('app/design/tailwind/theme')
2 |
3 | /** @type {import('tailwindcss').Config} */
4 | module.exports = {
5 | content: [
6 | './pages/**/*.{js,jsx,ts,tsx}',
7 | '../../packages/**/*.{js,jsx,ts,tsx}',
8 | ],
9 | plugins: [require('nativewind/tailwind/css')],
10 | important: 'html',
11 | theme: {
12 | ...theme,
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/apps/next/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true
17 | },
18 | "include": ["next-env.d.ts", "app-env.d.ts", "**/*.ts", "**/*.tsx"],
19 | "exclude": ["node_modules"]
20 | }
21 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solito-with-tailwind",
3 | "private": true,
4 | "workspaces": [
5 | "apps/*",
6 | "packages/*"
7 | ],
8 | "devDependencies": {
9 | "@types/react": "^18.2.21",
10 | "@types/react-native": "^0.72.2",
11 | "eslint": "^8.21.0",
12 | "prettier": "^3.0.3",
13 | "prettier-plugin-tailwindcss": "^0.5.4",
14 | "turbo": "^1.4.2",
15 | "typescript": "^5.2.2"
16 | },
17 | "scripts": {
18 | "native": "cd apps/expo && yarn start",
19 | "web": "cd apps/next && yarn next"
20 | },
21 | "resolutions": {
22 | "metro": "~0.76.7",
23 | "metro-resolver": "~0.76.7"
24 | },
25 | "nohoist": [
26 | "**/expo-router",
27 | "**/expo-router/**"
28 | ],
29 | "packageManager": "yarn@3.4.1"
30 | }
31 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/design/layout.tsx:
--------------------------------------------------------------------------------
1 | import { View } from 'react-native'
2 | import { styled } from 'nativewind'
3 |
4 | export const Row = styled(View, "flex-row")
5 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/design/tailwind/theme.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | /** @type {import('tailwindcss').Config['theme']} */
4 | const theme = {
5 | // edit your tailwind theme here!
6 | // https://tailwindcss.com/docs/adding-custom-styles
7 | }
8 |
9 | module.exports = {
10 | theme,
11 | }
12 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/design/view.tsx:
--------------------------------------------------------------------------------
1 | import { View as ReactNativeView } from 'react-native'
2 | import { styled } from 'nativewind'
3 |
4 | export const View = styled(ReactNativeView)
5 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/features/user/detail-screen.tsx:
--------------------------------------------------------------------------------
1 | import { createParam } from 'solito'
2 | import { TextLink } from 'solito/link'
3 | import { Text } from 'app/design/typography'
4 | import { View } from 'app/design/view'
5 |
6 | const { useParam } = createParam<{ id: string }>()
7 |
8 | export function UserDetailScreen() {
9 | const [id] = useParam('id')
10 |
11 | return (
12 |
13 | {`User ID: ${id}`}
14 | 👈 Go Home
15 |
16 | )
17 | }
18 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/index.ts:
--------------------------------------------------------------------------------
1 | // leave this blank
2 | // don't re-export files from this workspace. it'll break next.js tree shaking
3 | // https://github.com/vercel/next.js/issues/12557
4 | export {}
5 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/nativewind.d.ts:
--------------------------------------------------------------------------------
1 | import 'nativewind/types.d'
2 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.0",
3 | "name": "app",
4 | "main": "index.ts",
5 | "dependencies": {
6 | "moti": "latest",
7 | "nativewind": "2.0.11",
8 | "solito": "4.3.0"
9 | },
10 | "sideEffects": false
11 | }
12 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/provider/index.tsx:
--------------------------------------------------------------------------------
1 | import { SafeArea } from './safe-area'
2 |
3 | export function Provider({ children }: { children: React.ReactNode }) {
4 | return {children}
5 | }
6 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/provider/safe-area/index.tsx:
--------------------------------------------------------------------------------
1 | import { SafeAreaProvider } from 'react-native-safe-area-context'
2 |
3 | export const SafeArea = SafeAreaProvider
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/provider/safe-area/index.web.tsx:
--------------------------------------------------------------------------------
1 | // on Web, we don't use React Navigation, so we are going to avoid the safe area provider
2 | // instead, we just have a no-op here
3 | // for more, see: https://solito.dev/recipes/tree-shaking
4 |
5 | // if you need safe area hooks yourself, you can implement this yourself
6 | // however, you may be better off using the CSS selector for env(safe-area-inset-top) on Web
7 |
8 | // for more, see the `./use-safe-area.web.ts` file
9 |
10 | export const SafeArea = ({ children }: { children: React.ReactElement }) => (
11 | <>{children}>
12 | )
13 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/provider/safe-area/use-safe-area.ts:
--------------------------------------------------------------------------------
1 | import { useSafeAreaInsets } from 'react-native-safe-area-context'
2 |
3 | const useSafeArea = useSafeAreaInsets
4 |
5 | // `export { useSafeAreaInsets as useSafeArea }` breaks autoimport, so do this instead
6 | export { useSafeArea }
7 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/provider/safe-area/use-safe-area.web.ts:
--------------------------------------------------------------------------------
1 | // I don't use the real useSafeAreaInsets() hook, since
2 | // 1) the SafeAreaProvider forces you to render null on Web until it measures
3 | // 2) you might not need to support it, unless you're doing landscape stuff
4 | // 3) react-native-safe-area-context has a massive import on Web
5 | // see: https://github.com/th3rdwave/react-native-safe-area-context/pull/189#issuecomment-815274313
6 | // 4) most importantly, I think you can just use the env(safe-area-inset-bottom) CSS variable instead
7 | // after all, safe area code is few-and-far-between, so if you have to write some platform-speciifc code for it,
8 | // that is probably better than a massive bundle size for little benefit
9 |
10 | import type { useSafeArea as nativeHook } from './use-safe-area'
11 |
12 | const area = {
13 | bottom: 0,
14 | left: 0,
15 | right: 0,
16 | top: 0,
17 |
18 | // you could also use CSS env variables like below:
19 | // but you'll have to be sure to override the types for `useSafeArea`
20 | // and make sure to never add numbers and strings when you consue useSafeArea
21 | // just keep in mind that the env() doesn't work on older browsers I think
22 |
23 | // top: `env(safe-area-inset-top)`,
24 | // right: `env(safe-area-inset-right)`,
25 | // bottom: `env(safe-area-inset-bottom)`,
26 | // left: `env(safe-area-inset-left)`,
27 | }
28 |
29 | export function useSafeArea(): ReturnType {
30 | return area
31 | }
32 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/rnw-overrides.d.ts:
--------------------------------------------------------------------------------
1 | // override react-native types with react-native-web types
2 | import 'react-native'
3 |
4 | declare module 'react-native' {
5 | interface PressableStateCallbackType {
6 | hovered?: boolean
7 | focused?: boolean
8 | }
9 | interface ViewStyle {
10 | transitionProperty?: string
11 | transitionDuration?: string
12 | }
13 | interface TextProps {
14 | accessibilityComponentType?: never
15 | accessibilityTraits?: never
16 | accessibilityLevel?: number
17 | href?: string
18 | hrefAttrs?: {
19 | rel: 'noreferrer'
20 | target?: '_blank'
21 | }
22 | }
23 | interface ViewProps {
24 | accessibilityRole?: string
25 | href?: string
26 | hrefAttrs?: {
27 | rel: 'noreferrer'
28 | target?: '_blank'
29 | }
30 | onClick?: (e: React.MouseEvent) => void
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/tailwind.config.js:
--------------------------------------------------------------------------------
1 | // This is a dummy tailwind config file used to provide intellisense.
2 | // To configure your global tailwind settings, modify the imported theme object.
3 | const { theme } = require('app/design/tailwind/theme')
4 |
5 | /** @type {import('tailwindcss').Config} */
6 | module.exports = {
7 | content: [
8 | './**/*.{js,jsx,ts,tsx}',
9 | ],
10 | theme: {
11 | ...theme,
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/packages/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig"
3 | }
4 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strictNullChecks": true,
4 | "noUncheckedIndexedAccess": true,
5 | "paths": {
6 | "app/*": ["./packages/app/*"]
7 | },
8 | "jsx": "react-jsx"
9 | },
10 | "extends": "expo/tsconfig.base"
11 | }
12 |
--------------------------------------------------------------------------------
/example-monorepos/with-tailwind/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "pipeline": {
3 | "build": {
4 | "dependsOn": ["^build"],
5 | "outputs": [".next/**"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/image/author/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../build/image/author'
2 |
--------------------------------------------------------------------------------
/image/author/index.js:
--------------------------------------------------------------------------------
1 | export * from '../../build/image/author'
2 |
--------------------------------------------------------------------------------
/image/expo/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../../build/image/expo'
2 |
--------------------------------------------------------------------------------
/image/expo/index.js:
--------------------------------------------------------------------------------
1 | export * from '../../build/image/expo'
2 |
--------------------------------------------------------------------------------
/image/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/image/expo'
2 |
--------------------------------------------------------------------------------
/image/index.js:
--------------------------------------------------------------------------------
1 | export * from '../build/image/expo'
2 |
--------------------------------------------------------------------------------
/image/react-native-fast-image.js:
--------------------------------------------------------------------------------
1 | export * from '../build/image/fast'
2 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | ///
4 | ///
5 |
6 | export * from './build'
7 |
--------------------------------------------------------------------------------
/link/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/link'
2 |
--------------------------------------------------------------------------------
/link/index.js:
--------------------------------------------------------------------------------
1 | export * from '../build/link'
2 |
--------------------------------------------------------------------------------
/moti/app.js:
--------------------------------------------------------------------------------
1 | export * from '../build/moti/app'
2 |
--------------------------------------------------------------------------------
/moti/app.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/moti/app'
2 |
--------------------------------------------------------------------------------
/moti/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/moti'
2 |
--------------------------------------------------------------------------------
/moti/index.js:
--------------------------------------------------------------------------------
1 | export * from '../build/moti'
2 |
--------------------------------------------------------------------------------
/navigation/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/app/navigation'
2 |
--------------------------------------------------------------------------------
/navigation/index.js:
--------------------------------------------------------------------------------
1 | export * from '../build/app/navigation'
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "solito",
3 | "version": "4.4.1",
4 | "scripts": {
5 | "build": "expo-module build",
6 | "clean": "expo-module clean",
7 | "test": "expo-module test",
8 | "prepare": "expo-module prepare",
9 | "upgrade-expo": "bun ./scripts/upgrade-examples-expo.ts"
10 | },
11 | "devDependencies": {
12 | "@babel/preset-env": "^7.20.2",
13 | "@babel/preset-typescript": "^7.18.6",
14 | "@react-navigation/bottom-tabs": "7.0.0",
15 | "@react-navigation/drawer": "7.0.0",
16 | "@react-navigation/native": "7.0.0",
17 | "@react-navigation/native-stack": "7.0.0",
18 | "@types/jest": "^29.2.3",
19 | "@types/node": "^18.11.9",
20 | "@types/react": "^18.0.7",
21 | "concurrently": "^7.6.0",
22 | "eslint-config-nando": "^1.1.0",
23 | "expo-image": "latest",
24 | "expo-module-scripts": "^3.4.1",
25 | "jest": "^29.3.1",
26 | "moti": "^0.17.1",
27 | "next": "^13.5.5",
28 | "react-native": "^0.72.6",
29 | "react-native-fast-image": "^8.6.3"
30 | },
31 | "resolutions": {
32 | "@types/react": "^18.0.7",
33 | "react": "18.2.0",
34 | "react-dom": "18.2.0"
35 | },
36 | "license": "MIT",
37 | "bugs": {
38 | "url": "https://github.com/nandorojo/solito/issues"
39 | },
40 | "homepage": "https://github.com/nandorojo/solito.git#readme",
41 | "main": "build/index.js",
42 | "types": "index.d.ts",
43 | "sideEffects": false,
44 | "publishConfig": {
45 | "access": "public"
46 | },
47 | "author": {
48 | "email": "fernando@wearptos.com",
49 | "name": "Fernando Rojo",
50 | "url": "https://fernandotherojo.co"
51 | },
52 | "files": [
53 | "build/**/*",
54 | "index.d.ts",
55 | "src/**/*",
56 | "link/**/*",
57 | "router/**/*",
58 | "moti/**/*",
59 | "image/**/*",
60 | "navigation/**/*"
61 | ],
62 | "packageManager": "yarn@4.7.0",
63 | "dependencies": {
64 | "typescript": "^5.0.4"
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/params.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://params.com/params.json",
3 | "docs": {
4 | "main": "README.md",
5 | "sidebar": {
6 | "Welcome": "docs/docs/welcome.mdx",
7 | "Gradual Adoption": "docs/docs/gradual-adoption.md",
8 | "Methodology": "docs/docs/methodology.md",
9 | "Install": "docs/docs/install.md",
10 | "Minimal Starter": "docs/docs/starter.md",
11 | "Tailwind CSS Starter": "docs/docs/tailwind.mdx",
12 | "Expo Router Starter": "docs/docs/expo-router.mdx",
13 | "Link API": "docs/docs/usage/link.mdx",
14 | "Text Link API": "docs/docs/usage/text-link.mdx",
15 | "Moti Link API": "docs/docs/usage/moti-link.mdx",
16 | "Image API": "docs/docs/usage/image.mdx",
17 | "Use Router API": "docs/docs/usage/use-router.mdx",
18 | "Params API": "docs/docs/usage/params.mdx",
19 | "Use Link API": "docs/docs/usage/use-link.mdx",
20 | "Next.js App Router Overview": "docs/docs/app-directory/overview.mdx",
21 | "Next.js App Router Hooks": "docs/docs/app-directory/hooks.mdx",
22 | "New Route Guide": "docs/docs/guides/new-route.mdx",
23 | "Dynamic Route Guide": "docs/docs/guides/dynamic-route.mdx",
24 | "Expo Router Guide": "docs/docs/guides/expo-router.mdx",
25 | "Auth Guide": "docs/docs/guides/auth.mdx",
26 | "Redirects Recipe": "docs/docs/recipes/redirects.mdx",
27 | "Tree Shaking Recipe": "docs/docs/recipes/tree-shaking.mdx",
28 | "Use isFocused Recipe": "docs/docs/recipes/use-is-focused.mdx",
29 | "Scroll View Recipe": "docs/docs/recipes/scroll-view.mdx",
30 | "Deep Linking Recipe": "docs/docs/recipes/deep-linking.mdx",
31 | "Modals Recipe": "docs/docs/recipes/modals.mdx",
32 | "Deploying Recipe": "docs/docs/recipes/deploying.mdx",
33 | "Platform Code Recipe": "docs/docs/recipes/platform-code.mdx",
34 | "Icons Recipe": "docs/docs/recipes/icons.mdx",
35 | "Resources": "docs/docs/resources.mdx",
36 | "Version 4": "docs/docs/v4.mdx",
37 | "Version 2": "docs/docs/v2.mdx"
38 | },
39 | "youtube": {
40 | "video_id": "H1gSWXA3qfw"
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/router/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from '../build/router'
2 |
--------------------------------------------------------------------------------
/router/index.js:
--------------------------------------------------------------------------------
1 | export * from '../build/router'
2 |
--------------------------------------------------------------------------------
/scripts/install-examples.ts:
--------------------------------------------------------------------------------
1 | import concurrent from 'concurrently'
2 | import fs from 'fs'
3 | import path from 'path'
4 |
5 | const examples = fs.readdirSync(path.join(__dirname, '../example-monorepos'))
6 |
7 | console.log('running yarn in example monorepos')
8 |
9 | concurrent(
10 | examples.map((example, i) => {
11 | const appPath = path.join(__dirname, '../example-monorepos', example)
12 | return {
13 | command: `yarn --prefer-offline`,
14 | name: example,
15 | prefixColor: ['cyan', 'magenta', 'green', 'yellow', 'red'][i],
16 | cwd: appPath,
17 | }
18 | })
19 | )
20 |
--------------------------------------------------------------------------------
/scripts/upgrade-examples-expo.ts:
--------------------------------------------------------------------------------
1 | import * as child from 'child_process'
2 | import fs from 'fs'
3 | import path from 'path'
4 | import concurrent from 'concurrently'
5 |
6 | const examples = fs.readdirSync(path.join(__dirname, '../example-monorepos'))
7 |
8 | console.log('upgrading expo versions in examples...')
9 |
10 | concurrent(
11 | examples.map((example, i) => {
12 | const appPath = path.join(
13 | __dirname,
14 | '../example-monorepos',
15 | example,
16 | 'apps/expo'
17 | )
18 | return {
19 | command: `yarn --prefer-offline && expo upgrade && cd ../.. && yarn --prefer-offline`,
20 | name: example,
21 | prefixColor: ['cyan', 'magenta', 'green', 'yellow', 'red'][i],
22 | cwd: appPath,
23 | }
24 | })
25 | )
26 |
27 | // Promise.all([
28 | // examples.map((example) => {
29 | // const appPath = path.join(
30 | // __dirname,
31 | // '../example-monorepos',
32 | // example,
33 | // 'apps/expo'
34 | // )
35 | // return new Promise(async (resolve) =>
36 | // resolve(
37 | // child.execSync(`yarn && expo upgrade`, {
38 | // cwd: appPath,
39 | // stdio: 'inherit',
40 | // })
41 | // )
42 | // )
43 | // }),
44 | // ])
45 |
--------------------------------------------------------------------------------
/scripts/upgrade-examples.ts:
--------------------------------------------------------------------------------
1 | const child = require('child_process')
2 | import fs from 'fs'
3 | import path from 'path'
4 |
5 | const examples = fs.readdirSync(path.join(__dirname, '../example-monorepos'))
6 |
7 | console.log('upgrading solito examples...')
8 |
9 | Promise.all([
10 | examples.map((example) => {
11 | const appPath = path.join(
12 | __dirname,
13 | '../example-monorepos',
14 | example,
15 | 'packages/app'
16 | )
17 | return child.exec(`yarn add solito`, {
18 | cwd: appPath,
19 | })
20 | }),
21 | ])
22 |
--------------------------------------------------------------------------------
/src/app/navigation/index.ts:
--------------------------------------------------------------------------------
1 | export * from './use-router'
2 | export * from './use-params'
3 | export * from './use-search-params'
4 | export * from './use-pathname'
5 | export * from './use-link'
6 | export { default as useUpdateSearchParams } from './use-update-search-params'
7 |
--------------------------------------------------------------------------------
/src/app/navigation/use-link.ts:
--------------------------------------------------------------------------------
1 | import { GestureResponderEvent, Platform } from 'react-native'
2 |
3 | import { useRouter } from './use-router'
4 |
5 | export function useLink({
6 | href,
7 | replace,
8 | experimental,
9 | }: {
10 | href: string
11 | replace?: boolean
12 | } & Parameters['replace']>[1]) {
13 | const router = useRouter()
14 |
15 | // https://github.com/react-navigation/react-navigation/blob/main/packages/native/src/useLinkProps.tsx#L64
16 | const onPress = (
17 | e?: React.MouseEvent | GestureResponderEvent
18 | ) => {
19 | let shouldHandle = false
20 |
21 | if (Platform.OS !== 'web' || !e) {
22 | shouldHandle = e ? !e.defaultPrevented : true
23 | } else if (
24 | !e.defaultPrevented && // onPress prevented default
25 | // @ts-expect-error: these properties exist on web, but not in React Native
26 | !(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) && // ignore clicks with modifier keys
27 | // @ts-expect-error: these properties exist on web, but not in React Native
28 | (e.button == null || e.button === 0) && // ignore everything but left clicks
29 | // @ts-expect-error: these properties exist on web, but not in React Native
30 | [undefined, null, '', 'self'].includes(e.currentTarget?.target) // let browser handle "target=_blank" etc.
31 | ) {
32 | e.preventDefault()
33 | shouldHandle = true
34 | }
35 |
36 | if (shouldHandle) {
37 | if (href === '#') {
38 | // this is a way on web to stay on the same page
39 | // useful for conditional hrefs
40 | return
41 | }
42 | if (replace) {
43 | router.replace(href, { experimental })
44 | } else {
45 | router.push(href)
46 | }
47 | }
48 | }
49 |
50 | return {
51 | accessibilityRole: 'link' as const,
52 | onPress,
53 | href,
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-params.native.ts:
--------------------------------------------------------------------------------
1 | export default () => undefined
2 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-params.ts:
--------------------------------------------------------------------------------
1 | import { useParams } from 'next/navigation'
2 |
3 | export default () => {
4 | // need to cast this type to appease TS, idk why
5 | return useParams() as Record | undefined
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-pathname.native.ts:
--------------------------------------------------------------------------------
1 | export default () => undefined
2 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-pathname.ts:
--------------------------------------------------------------------------------
1 | import { usePathname } from 'next/navigation'
2 |
3 | export default () => usePathname()
4 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-router.ts:
--------------------------------------------------------------------------------
1 | import type { useRouter } from 'next/navigation'
2 |
3 | export const useNextAppDirRouter = ():
4 | | ReturnType
5 | | undefined => undefined
6 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-router.web.ts:
--------------------------------------------------------------------------------
1 | export { useRouter as useNextAppDirRouter } from 'next/navigation'
2 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-search-params.native.ts:
--------------------------------------------------------------------------------
1 | export default () => undefined
2 |
--------------------------------------------------------------------------------
/src/app/navigation/use-next-search-params.ts:
--------------------------------------------------------------------------------
1 | import { useSearchParams } from 'next/navigation'
2 |
3 | export default () => {
4 | // need to cast this type to appease TS, idk why
5 | return useSearchParams() as ReturnType | undefined
6 | }
7 |
--------------------------------------------------------------------------------
/src/app/navigation/use-params.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 | import useNextParams from './use-next-params'
3 | import { useRoute } from '../../params/use-route'
4 |
5 | type OrArray = Type | Type[]
6 |
7 | export function useParams<
8 | Type extends Record>,
9 | TypesSettings extends 'enforce-safety' | 'ignore-safety' = 'enforce-safety',
10 | CatchAllSegments extends Partial> = {}
11 | >(
12 | _settings: {
13 | types?: TypesSettings
14 | catchAllSegments?: CatchAllSegments
15 | } = {}
16 | ) {
17 | type Returns = {
18 | // people need to manually type check that it's not an array or a string, since the URL could provide this
19 | [Key in keyof Type]: TypesSettings extends 'ignore-safety'
20 | ? Type[Key]
21 | : Key extends keyof CatchAllSegments
22 | ? Array
23 | : Type[Key] | string
24 | }
25 | if (Platform.OS === 'web') {
26 | return useNextParams() as Returns
27 | }
28 |
29 | const route = useRoute()
30 |
31 | if (!route) {
32 | console.error(
33 | `[useParams] route is undefined. Is your ${Platform.OS} app properly configured for React Navigation?`
34 | )
35 | }
36 |
37 | return route?.params as Returns
38 | }
39 |
--------------------------------------------------------------------------------
/src/app/navigation/use-pathname.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 | import useNextPathname from './use-next-pathname'
3 | import { useRoute } from '../../params/use-route'
4 |
5 | // TODO test this with react navigation and expo router. does it work?
6 | export function usePathname() {
7 | if (Platform.OS === 'web') {
8 | return useNextPathname()
9 | }
10 |
11 | const path = useRoute()?.path
12 |
13 | return path?.includes('?') ? path.split('?')[0] : path
14 | }
15 |
--------------------------------------------------------------------------------
/src/app/navigation/use-search-params.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 | import { useRoute } from '../../params/use-route'
3 | import useNextSearchParams from './use-next-search-params'
4 | import { ReadonlyURLSearchParams } from 'next/navigation'
5 | import { useMemo } from 'react'
6 |
7 | export function useSearchParams>() {
8 | type Returns =
9 | | (ReadonlyURLSearchParams & {
10 | get: (key: keyof Type) => string | null
11 | getAll: (key: keyof Type) => string[]
12 | has: (key: keyof Type) => boolean
13 | })
14 | | undefined
15 | if (Platform.OS === 'web') {
16 | return useNextSearchParams() as Returns
17 | }
18 |
19 | const route = useRoute()
20 |
21 | if (!route) {
22 | console.error(
23 | `[useParams] route is undefined. Is your ${Platform.OS} app properly configured for React Navigation?`
24 | )
25 | }
26 |
27 | const params = route?.params as Type | undefined
28 |
29 | if (__DEV__) {
30 | const nonStringParamValues = Object.entries(params || {})
31 | .map(([key, value]) => {
32 | if (typeof value !== 'string') {
33 | return `${key}: ${JSON.stringify(value)}`
34 | }
35 | return undefined
36 | })
37 | .filter(Boolean)
38 |
39 | if (nonStringParamValues.length) {
40 | throw new Error(`[useSearchParams][solito] Error found in the "${
41 | route?.name ?? 'unknown'
42 | }" screen (path ${
43 | route?.path ?? 'unknown'
44 | }). You used non-string parameters for the following params:
45 |
46 | ${nonStringParamValues.join('\n')}
47 |
48 | Due to constraints from Next.js, this is not valid in Solito. Please refactor your code to use strings for screen parameters.
49 | `)
50 | }
51 | }
52 |
53 | return useMemo(() => params && new URLSearchParams(params), [params])
54 | }
55 |
--------------------------------------------------------------------------------
/src/app/navigation/use-update-search-params.native.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { useRouter } from './use-router'
3 | import { useNavigation } from '../../router/use-navigation'
4 | import { Platform } from 'react-native'
5 | import { UseUpdateSearchParamsReturns } from './use-update-search-params.types'
6 |
7 | export default function <
8 | Type extends Record = Record
9 | >(): UseUpdateSearchParamsReturns {
10 | const navigation = useNavigation()
11 |
12 | return useCallback(
13 | (params) => {
14 | navigation?.setParams(params)
15 | },
16 | [navigation]
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/navigation/use-update-search-params.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { useRouter } from './use-router'
3 | import { useNavigation } from '../../router/use-navigation'
4 | import { Platform } from 'react-native'
5 | import { UseUpdateSearchParamsReturns } from './use-update-search-params.types'
6 | import { usePathname } from './use-pathname'
7 | import { useSearchParams } from 'next/navigation'
8 |
9 | export default function <
10 | Type extends Record = Record
11 | >(): UseUpdateSearchParamsReturns {
12 | const router = useRouter()
13 | const pathname = usePathname()
14 | const searchParams = useSearchParams()
15 |
16 | return useCallback(
17 | (params, options) => {
18 | const next = new URLSearchParams(searchParams?.toString())
19 |
20 | let shouldReplace = false
21 | Object.entries(params).forEach(([key, value]) => {
22 | if (next.has(key)) {
23 | shouldReplace = true
24 | }
25 | if (value == null) {
26 | next.delete(key)
27 | } else {
28 | next.set(key, value)
29 | }
30 | })
31 | const action =
32 | router[options?.webBehavior ?? (shouldReplace ? 'replace' : 'push')]
33 |
34 | const stringifiedNext = next.toString();
35 | action(`${pathname}${stringifiedNext ? `?${stringifiedNext}` : ''}`)
36 | },
37 | [router]
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/app/navigation/use-update-search-params.types.ts:
--------------------------------------------------------------------------------
1 | export type UseUpdateSearchParamsReturns<
2 | Params extends Record
3 | > = (
4 | params: Partial,
5 | options?: {
6 | webBehavior?: 'replace' | 'push'
7 | }
8 | ) => void
9 |
--------------------------------------------------------------------------------
/src/helpers/merge-refs.ts:
--------------------------------------------------------------------------------
1 | // credit: https://github.com/gregberge/react-merge-refs/blob/main/src/index.tsx
2 | export function mergeRefs(
3 | refs: Array | React.LegacyRef>
4 | ): React.RefCallback {
5 | return (value) => {
6 | refs.forEach((ref) => {
7 | if (typeof ref === 'function') {
8 | ref(value)
9 | } else if (ref != null) {
10 | ;(ref as React.MutableRefObject).current = value
11 | }
12 | })
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/helpers/use-stable-callback.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useMemo, useEffect } from 'react'
2 |
3 | /**
4 | * A custom hook that converts a callback to a ref to avoid triggering re-renders when passed as a
5 | * prop or avoid re-executing effects when passed as a dependency
6 | */
7 | function useStableCallback any>(
8 | callback: T | undefined
9 | ): T {
10 | const callbackRef = useRef(callback)
11 |
12 | useEffect(() => {
13 | callbackRef.current = callback
14 | })
15 |
16 | // https://github.com/facebook/react/issues/19240
17 | return useMemo(() => ((...args) => callbackRef.current?.(...args)) as T, [])
18 | }
19 |
20 | export { useStableCallback }
21 |
--------------------------------------------------------------------------------
/src/image/author/index.ts:
--------------------------------------------------------------------------------
1 | export type { SolitoImageProps } from '../image.types'
2 |
3 | export * from '../create-solito-image'
4 | export * from '../use-solito-image'
5 |
--------------------------------------------------------------------------------
/src/image/context.tsx:
--------------------------------------------------------------------------------
1 | import { ContextType, createContext, useContext, useState } from 'react'
2 |
3 | import { imageConfigDefault } from './helpers'
4 |
5 | const SolitoImageContext =
6 | createContext>(imageConfigDefault)
7 |
8 | export const SolitoImageProvider = ({
9 | children,
10 | ...rest
11 | }: {
12 | children: React.ReactNode
13 | } & ContextType) => {
14 | const parent = useContext(SolitoImageContext)
15 | const [context] = useState(() => ({
16 | ...parent,
17 | ...rest,
18 | }))
19 |
20 | return (
21 |
22 | {children}
23 |
24 | )
25 | }
26 |
27 | export const useSolitoImageContext = () => useContext(SolitoImageContext)
28 |
--------------------------------------------------------------------------------
/src/image/create-solito-image.tsx:
--------------------------------------------------------------------------------
1 | import React, { ComponentType, createElement, forwardRef } from 'react'
2 |
3 | import { SolitoImageProps } from './image.types'
4 | import { useSolitoImage } from './use-solito-image'
5 |
6 | export function createSolitoImage<
7 | C extends ComponentType>
8 | >(Component: C) {
9 | return forwardRef, SolitoImageProps>(function Img(
10 | props,
11 | ref
12 | ) {
13 | const imageProps = useSolitoImage(props)
14 | return createElement(Component, { ...imageProps, ref } as any)
15 | })
16 | }
17 |
--------------------------------------------------------------------------------
/src/image/default-loader.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 | import { ImageConfigComplete } from './types'
3 |
4 | export type ImageLoaderProps = {
5 | src: string
6 | width: number
7 | quality?: number
8 | }
9 |
10 | export type ImageLoaderPropsWithConfig = ImageLoaderProps & {
11 | config: Readonly
12 | }
13 |
14 | export function defaultLoader({
15 | config,
16 | src,
17 | width,
18 | quality,
19 | }: ImageLoaderPropsWithConfig): string {
20 | if (process.env.NODE_ENV !== 'production') {
21 | const missingValues = []
22 |
23 | // these should always be provided but make sure they are
24 | if (!src) missingValues.push('src')
25 | if (!width) missingValues.push('width')
26 |
27 | if (missingValues.length > 0) {
28 | throw new Error(
29 | `Solito Image requires ${missingValues.join(
30 | ', '
31 | )} to be provided. Make sure you pass them as props to the \`solito/image\` component. Received: ${JSON.stringify(
32 | { src, width, quality }
33 | )}`
34 | )
35 | }
36 |
37 | if (src.startsWith('/') && !config.nextJsURL) {
38 | throw new Error(
39 | `[solito/image] Please add the "nextJsURL" prop to your to use relative paths.
40 |
41 | Error due to image source "${src}".`
42 | )
43 | }
44 | }
45 |
46 | if (src.startsWith('/') && src.endsWith('.svg')) {
47 | // relative SVG paths are raw
48 | return `${config.nextJsURL}${src}`
49 | }
50 |
51 | if (src.startsWith('http')) {
52 | return src
53 | }
54 |
55 | if (src.startsWith('file://')) {
56 | return src
57 | }
58 |
59 | return `${config.nextJsURL}${config.path}?url=${encodeURIComponent(
60 | src
61 | )}&w=${width}&q=${quality || 75}`
62 | }
63 |
64 | // We use this to determine if the import is the default loader
65 | // or a custom loader defined by the user in next.config.js
66 | // defaultLoader.__next_img_default = true
67 |
--------------------------------------------------------------------------------
/src/image/expo/image.tsx:
--------------------------------------------------------------------------------
1 | import { Image } from 'expo-image'
2 | import { forwardRef } from 'react'
3 |
4 | import { SolitoImageProps } from '../image.types'
5 | import { useSolitoImage } from '../use-solito-image'
6 |
7 | const SolitoImage = forwardRef(function Img(
8 | props,
9 | ref
10 | ) {
11 | const {
12 | onLoadingComplete,
13 | onError,
14 | resizeMode = 'contain',
15 | ...imageProps
16 | } = useSolitoImage(props)
17 |
18 | return (
19 | onLoadingComplete(e.source))}
24 | onError={onError}
25 | ref={ref}
26 | style={imageProps.style}
27 | contentPosition={props.contentPosition}
28 | />
29 | )
30 | })
31 |
32 | export type SolitoImageRef = Image
33 |
34 | export default SolitoImage
35 |
--------------------------------------------------------------------------------
/src/image/expo/index.ts:
--------------------------------------------------------------------------------
1 | import type { SolitoImageRef } from './image'
2 | export type SolitoImage = SolitoImageRef
3 |
4 | export { default as SolitoImage } from './image'
5 |
6 | export { SolitoImageProvider } from '../context'
7 |
--------------------------------------------------------------------------------
/src/image/fast/fast.tsx:
--------------------------------------------------------------------------------
1 | import { ComponentProps } from 'react'
2 | import FastImage from 'react-native-fast-image'
3 |
4 | import { SolitoImageProps } from '../image.types'
5 | import { useSolitoImage } from '../use-solito-image'
6 |
7 | export default function SolitoFastImage(
8 | props: Omit &
9 | Pick, 'style' | 'resizeMode' | 'testID'>
10 | ) {
11 | const {
12 | source,
13 | resizeMode,
14 | onLoadingComplete,
15 | onError,
16 | style,
17 | ...imageProps
18 | } = useSolitoImage(props as SolitoImageProps)
19 |
20 | return (
21 | onLoadingComplete(e.nativeEvent))}
34 | onError={onError}
35 | // @ts-expect-error this is fine
36 | style={style}
37 | {...imageProps}
38 | />
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/image/fast/fast.web.tsx:
--------------------------------------------------------------------------------
1 | export { default } from '../expo/image.web'
2 |
--------------------------------------------------------------------------------
/src/image/fast/index.ts:
--------------------------------------------------------------------------------
1 | import SolitoImage from './fast'
2 |
3 | export { SolitoImage }
4 | export { SolitoImageProvider } from '../context'
5 |
--------------------------------------------------------------------------------
/src/image/image.types.ts:
--------------------------------------------------------------------------------
1 | import { ImageContentPosition, ImageProps } from 'expo-image'
2 | import type NextImage from 'next/image'
3 | import type { ImageStyle } from 'react-native'
4 |
5 | export type AccessibilityProp = key extends `aria-${string}`
6 | ? key
7 | : key extends `accessibility${Capitalize}`
8 | ? key
9 | : never
10 |
11 | type NextImageProps = React.ComponentProps
12 |
13 | export type SolitoImageProps = Pick<
14 | NextImageProps,
15 | | AccessibilityProp
16 | | 'alt'
17 | | 'blurDataURL'
18 | | 'placeholder'
19 | | 'loader'
20 | | 'priority'
21 | | 'loading'
22 | | 'sizes'
23 | | 'quality'
24 | | 'crossOrigin'
25 | | 'referrerPolicy'
26 | | 'quality'
27 | | 'unoptimized'
28 | > & {
29 | style?: ImageStyle
30 | } & (
31 | | {
32 | src: string
33 | height: number
34 | width: number
35 | fill?: false
36 | }
37 | | {
38 | src: string
39 | height?: number
40 | width?: number
41 | fill: true
42 | }
43 | | {
44 | src: Exclude | number
45 | height?: number
46 | width?: number
47 | fill?: boolean
48 | }
49 | ) &
50 | Pick<
51 | ImageProps,
52 | | 'onLayout'
53 | | 'contentFit'
54 | | 'resizeMode'
55 | | 'transition'
56 | | 'recyclingKey'
57 | | AccessibilityProp
58 | > & {
59 | onLoadingComplete?: (info: { height: number; width: number }) => void
60 | onError?: () => void
61 | fill?: boolean
62 | contentPosition?: ImageContentPosition
63 | }
64 |
--------------------------------------------------------------------------------
/src/image/types.ts:
--------------------------------------------------------------------------------
1 | export type ImageConfigComplete = {
2 | deviceSizes: number[]
3 | imageSizes: number[]
4 | /**
5 | * Example: `https://beatgig.com` or `http://localhost:3000`
6 | *
7 | * This is the URL to your Next.js app, used on iOS & Android to resolve relative URLs.
8 | *
9 | * For example, if you have `public/image.png` in your Next.js app, and your URL is `https://beatgig.com`,
10 | * then you can use `` and it will get the image from the next.js site.
11 | *
12 | * Docs: https://solito.dev/usage/image#solitoimageprovider-
13 | */
14 | nextJsURL?: `http:${string}` | `https:${string}`
15 | /**
16 | * You probably shouldn't touch this. It's only used on native to get your images.
17 | *
18 | * Default: `'/_next/image'`. You must set nextJsURL for this to work.
19 | */
20 | path?: string
21 | loader?: (info: { src: string; width: number; quality: number }) => string
22 | }
23 |
24 | export type ImageConfig = ImageConfigComplete & {
25 | allSizes: number[]
26 | }
27 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types/solito-page'
2 | export * from './middleware/provider'
3 | export * from './params'
4 |
--------------------------------------------------------------------------------
/src/link/LinkCoreProps.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import React from 'react'
3 | import type { ComponentProps } from 'react'
4 | import { NextLink } from './next-link'
5 |
6 | export type LinkCoreProps = {
7 | children: React.ReactNode
8 | } & Omit<
9 | ComponentProps,
10 | 'passHref' | 'replace' | 'legacyBehavior'
11 | > &
12 | (
13 | | {
14 | replace?: false
15 | experimental?: undefined
16 | }
17 | | {
18 | replace: true
19 | experimental?: {
20 | nativeBehavior: 'stack-replace'
21 | isNestedNavigator: boolean
22 | }
23 | }
24 | )
25 |
--------------------------------------------------------------------------------
/src/link/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './core'
2 | export * from './text-link'
3 | export * from './link'
4 | export * from './use-custom-link'
5 |
--------------------------------------------------------------------------------
/src/link/link.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { Platform, Pressable, ViewProps, View } from 'react-native'
3 |
4 | import { LinkCore } from './core'
5 | import { LinkCoreProps } from './LinkCoreProps'
6 |
7 | type LinkProps = LinkCoreProps & { viewProps?: ViewProps }
8 |
9 | function Link({ viewProps, ...props }: LinkProps) {
10 | return (
11 |
19 | )
20 | }
21 |
22 | export { Link }
23 | export type { LinkProps }
24 |
--------------------------------------------------------------------------------
/src/link/linking.ts:
--------------------------------------------------------------------------------
1 | import { Linking } from 'react-native'
2 |
3 | export const openURL = (url: string) => Linking.openURL(url)
4 |
--------------------------------------------------------------------------------
/src/link/linking.web.ts:
--------------------------------------------------------------------------------
1 | // noop, not supported on web
2 | export const openURL = (url: string) => {}
3 |
--------------------------------------------------------------------------------
/src/link/next-link.tsx:
--------------------------------------------------------------------------------
1 | import type Link from 'next/link'
2 |
3 | export const NextLink = (() => {
4 | return <>>
5 | }) as any as typeof Link
6 |
--------------------------------------------------------------------------------
/src/link/next-link.web.tsx:
--------------------------------------------------------------------------------
1 | export { default as NextLink } from 'next/link'
2 |
--------------------------------------------------------------------------------
/src/link/text-link.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { TextProps, Text } from 'react-native'
3 |
4 | import { LinkCore } from './core'
5 | import { LinkCoreProps } from './LinkCoreProps'
6 |
7 | type TextLinkProps = LinkCoreProps & { textProps?: TextProps }
8 |
9 | function TextLink({ textProps, ...props }: TextLinkProps) {
10 | return (
11 |
16 | )
17 | }
18 |
19 | export { TextLink }
20 | export type { TextLinkProps }
21 |
--------------------------------------------------------------------------------
/src/middleware/context.tsx:
--------------------------------------------------------------------------------
1 | import { useLinkTo } from '@react-navigation/native'
2 | import { createContext } from 'react'
3 |
4 | import { MiddlewareContextType } from './types'
5 |
6 | export const MiddlewareContext = createContext({
7 | useLinkTo,
8 | // useLinkProps,
9 | })
10 |
--------------------------------------------------------------------------------
/src/middleware/context.web.tsx:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | import { MiddlewareContextType } from './types'
4 |
5 | function error() {
6 | throw new Error(
7 | '[solito] useLinkTo should not be called on Web. Is next/router defined?'
8 | )
9 | }
10 |
11 | export const MiddlewareContext = createContext({
12 | useLinkTo() {
13 | return error
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/src/middleware/provider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { MiddlewareContext } from './context'
4 | import { MiddlewareContextType } from './types'
5 |
6 | type Props = {
7 | middleware: MiddlewareContextType
8 | }
9 |
10 | export function SolitoProvider({
11 | children,
12 | middleware,
13 | }: { children: React.ReactNode } & Props) {
14 | return (
15 |
16 | {children}
17 |
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/middleware/types.ts:
--------------------------------------------------------------------------------
1 | import type { useLinkTo } from '@react-navigation/native'
2 |
3 | export type MiddlewareContextType = {
4 | useLinkTo?: typeof useLinkTo
5 | }
6 |
--------------------------------------------------------------------------------
/src/moti/app.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { MotiPressableProps, MotiPressable } from 'moti/interactions'
3 | import { forwardRef } from 'react'
4 | import type { View } from 'react-native'
5 |
6 | import { useLink } from '../app/navigation/use-link'
7 |
8 | type UseLinkProps = Parameters[0]
9 |
10 | export type MotiLinkProps = UseLinkProps &
11 | Omit<
12 | MotiPressableProps,
13 | // you can't pass any props that will be overridden by useLink
14 | | keyof UseLinkProps
15 | | keyof Pick, 'href' | 'accessibilityRole'>
16 | >
17 |
18 | export const MotiLink = forwardRef((props, ref) => {
19 | const { onPress, ...linkProps } = useLink(props)
20 |
21 | return (
22 | {
26 | // @ts-expect-error no event argument
27 | // we let users pass an onPress prop, in case they want to preventDefault()
28 | props.onPress?.(e)
29 |
30 | onPress(e)
31 | }}
32 | ref={ref}
33 | />
34 | )
35 | })
36 |
37 | MotiLink.displayName = 'MotiLink'
38 |
--------------------------------------------------------------------------------
/src/moti/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './link'
2 |
--------------------------------------------------------------------------------
/src/moti/link.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 | import { MotiPressableProps, MotiPressable } from 'moti/interactions'
3 | import { forwardRef } from 'react'
4 | import type { View } from 'react-native'
5 |
6 | import { useLink, UseLinkProps } from '../link/use-custom-link'
7 |
8 | export type MotiLinkProps = UseLinkProps &
9 | Omit<
10 | MotiPressableProps,
11 | // you can't pass any props that will be overridden by useLink
12 | | keyof UseLinkProps
13 | | keyof Pick, 'href' | 'accessibilityRole'>
14 | >
15 |
16 | export const MotiLink = forwardRef((props, ref) => {
17 | const { onPress, ...linkProps } = useLink(props)
18 |
19 | return (
20 | {
24 | // @ts-expect-error no event argument
25 | // we let users pass an onPress prop, in case they want to preventDefault()
26 | props.onPress?.(e)
27 |
28 | onPress?.(e)
29 | }}
30 | ref={ref}
31 | />
32 | )
33 | })
34 |
35 | MotiLink.displayName = 'MotiLink'
36 |
--------------------------------------------------------------------------------
/src/params/router.ts:
--------------------------------------------------------------------------------
1 | import type Router from 'next/router'
2 |
3 | export default undefined as any as typeof Router
4 |
--------------------------------------------------------------------------------
/src/params/router.web.ts:
--------------------------------------------------------------------------------
1 | export { default } from 'next/router'
2 |
--------------------------------------------------------------------------------
/src/params/use-route.ts:
--------------------------------------------------------------------------------
1 | import { NavigationRouteContext } from '@react-navigation/native'
2 | import { useContext } from 'react'
3 |
4 | export const useRoute = () => useContext(NavigationRouteContext)
5 |
--------------------------------------------------------------------------------
/src/params/use-route.web.ts:
--------------------------------------------------------------------------------
1 | export const useRoute = () => undefined
2 |
--------------------------------------------------------------------------------
/src/params/use-router.ts:
--------------------------------------------------------------------------------
1 | import { NextRouter } from 'next/router'
2 |
3 | export const useRouter = () => undefined as NextRouter | undefined
4 |
--------------------------------------------------------------------------------
/src/params/use-router.web.ts:
--------------------------------------------------------------------------------
1 | import { useRouter as _useRouter } from 'next/router';
2 |
3 | export const useRouter = () => _useRouter();
--------------------------------------------------------------------------------
/src/router/index.ts:
--------------------------------------------------------------------------------
1 | export * from './parse-next-path'
2 | export * from './use-router'
3 |
--------------------------------------------------------------------------------
/src/router/next-router.ts:
--------------------------------------------------------------------------------
1 | import Router from 'next/router'
2 |
3 | export const NextRouter: typeof Router | undefined = Router
4 |
--------------------------------------------------------------------------------
/src/router/next-router.web.ts:
--------------------------------------------------------------------------------
1 | export const NextRouter = undefined
2 |
--------------------------------------------------------------------------------
/src/router/parse-next-path.test.ts:
--------------------------------------------------------------------------------
1 | import { parseNextPath } from './parse-next-path'
2 |
3 | describe('parseNextPath', () => {
4 | it('should handle arrays', () => {
5 | expect(
6 | parseNextPath({
7 | pathname: '/',
8 | query: {
9 | ids: [1, 2],
10 | },
11 | })
12 | ).toEqual('/?ids=1&ids=2')
13 | })
14 |
15 | it('supports dynamic routes', () => {
16 | expect(
17 | parseNextPath({
18 | pathname: '/[id]',
19 | query: {
20 | id: 1,
21 | },
22 | })
23 | ).toEqual('/1')
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/src/router/parse-next-path.ts:
--------------------------------------------------------------------------------
1 | import type { NextRouter } from 'next/router'
2 |
3 | const parseNextPath = (from: Parameters[0]) => {
4 | let path = (typeof from == 'string' ? from : from.pathname) || ''
5 |
6 | // replace each instance of [key] with the corresponding value from query[key]
7 | // this ensures we're navigating to the correct URL
8 | // it currently ignores [[...param]]
9 | // but I can't see why you would use this with RN + Next.js
10 | if (typeof from == 'object' && from.query && typeof from.query == 'object') {
11 | const query = { ...from.query }
12 | // replace dynamic routes
13 | // and [...param] syntax
14 | for (const key in query) {
15 | if (path.includes(`[${key}]`)) {
16 | path = path.replace(`[${key}]`, `${query[key] ?? ''}`)
17 | delete query[key]
18 | } else if (path.includes(`[...${key}]`)) {
19 | const values = query[key]
20 | if (Array.isArray(values)) {
21 | path = path.replace(`[...${key}]`, values.join('/'))
22 | delete query[key]
23 | }
24 | }
25 | }
26 |
27 | if (Object.keys(query).length) {
28 | // add query param separator
29 | path += '?'
30 | for (const key in query) {
31 | const value = query[key]
32 | if (Array.isArray(value)) {
33 | value.forEach((item) => {
34 | path += `${key}=${item}&`
35 | })
36 | } else if (value != null) {
37 | path += `${key}=${value}&`
38 | }
39 | }
40 | if (path.endsWith('&') || path.endsWith('?')) {
41 | path = path.slice(0, -1)
42 | }
43 | }
44 | }
45 |
46 | return path
47 | }
48 |
49 | export { parseNextPath }
50 |
--------------------------------------------------------------------------------
/src/router/replace-helpers.ts:
--------------------------------------------------------------------------------
1 | import {
2 | StackActions,
3 | getStateFromPath,
4 | getActionFromState,
5 | LinkingContext,
6 | } from '@react-navigation/native'
7 |
8 | export { LinkingContext, StackActions, getStateFromPath, getActionFromState }
9 |
--------------------------------------------------------------------------------
/src/router/replace-helpers.web.ts:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react'
2 |
3 | const LinkingContext = createContext({
4 | options: undefined,
5 | })
6 |
7 | let StackActions, getStateFromPath, getActionFromState
8 |
9 | export { LinkingContext, StackActions, getStateFromPath, getActionFromState }
10 |
--------------------------------------------------------------------------------
/src/router/use-link-to.ts:
--------------------------------------------------------------------------------
1 | import { useLinkTo as useNativeLinkTo } from '@react-navigation/native'
2 | import { useContext } from 'react'
3 |
4 | import { MiddlewareContext } from '../middleware/context'
5 |
6 | export function useLinkTo() {
7 | const hook = useContext(MiddlewareContext).useLinkTo ?? useNativeLinkTo
8 |
9 | return hook()
10 | }
11 |
--------------------------------------------------------------------------------
/src/router/use-link-to.web.ts:
--------------------------------------------------------------------------------
1 | const noOp = () => {
2 | throw new Error(
3 | '[use-link-to] is not supported on the web. Something went wrong if you called this.'
4 | )
5 | }
6 |
7 | /**
8 | * @deprecated imported from the wrong file. Use `use-link-to` instead.
9 | */
10 | export const useLinkTo = () => noOp
11 |
--------------------------------------------------------------------------------
/src/router/use-navigation.ts:
--------------------------------------------------------------------------------
1 | import {
2 | NavigationContext,
3 | NavigationContainerRefContext,
4 | } from '@react-navigation/core'
5 | import { useContext } from 'react'
6 |
7 | export const useNavigation = () => {
8 | const root = useContext(NavigationContainerRefContext)
9 | const navigation = useContext(NavigationContext)
10 |
11 | if (navigation === undefined && root === undefined) {
12 | throw new Error(
13 | "Couldn't find a navigation object. Is your component inside NavigationContainer?"
14 | )
15 | }
16 |
17 | return navigation !== null && navigation !== void 0 ? navigation : root
18 | }
19 |
--------------------------------------------------------------------------------
/src/router/use-navigation.web.ts:
--------------------------------------------------------------------------------
1 | export const useNavigation = () => undefined
2 |
--------------------------------------------------------------------------------
/src/router/use-next-router.ts:
--------------------------------------------------------------------------------
1 | import type { useRouter } from 'next/router'
2 |
3 | export const useNextRouter = (): ReturnType | undefined =>
4 | undefined
5 |
--------------------------------------------------------------------------------
/src/router/use-next-router.web.ts:
--------------------------------------------------------------------------------
1 | export { useRouter as useNextRouter } from 'next/router'
2 |
--------------------------------------------------------------------------------
/src/types/solito-page.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/ban-types */
2 | import type { NextPage } from 'next'
3 | import type { AppProps } from 'next/app'
4 |
5 | interface SolitoCustomNavigationOptions {}
6 |
7 | interface SolitoNavigationOptions extends SolitoCustomNavigationOptions {
8 | previousPagePath?: string | null
9 | }
10 |
11 | type NavigationOptions =
12 | | SolitoNavigationOptions
13 | | ((router?: AppProps['router']) => SolitoNavigationOptions)
14 |
15 | type SolitoPage = NextPage
& {
16 | navigationOptions?: NavigationOptions
17 | getLayout?: (
18 | page: React.ReactNode,
19 | options?: NavigationOptions
20 | ) => React.ReactNode
21 | }
22 |
23 | export type SolitoAppProps
= Omit, 'Component'> & {
24 | Component: AppProps['Component'] & {
25 | navigationOptions?: NavigationOptions
26 | getLayout?: (
27 | page: React.ReactNode,
28 | options?: NavigationOptions
29 | ) => React.ReactNode
30 | }
31 | }
32 |
33 | export type {
34 | SolitoPage,
35 | SolitoNavigationOptions,
36 | SolitoCustomNavigationOptions,
37 | }
38 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "expo-module-scripts/tsconfig.base",
3 | "compilerOptions": {
4 | "outDir": "./build",
5 | "skipLibCheck": true,
6 | "strictNullChecks": true,
7 | "allowSyntheticDefaultImports": true,
8 | "moduleResolution": "node",
9 | "strict": true,
10 | "experimentalDecorators": true,
11 | "noUnusedLocals": false,
12 | "jsx": "react-jsx"
13 | },
14 | "include": ["./src"],
15 | "exclude": ["**/__mocks__/*", "**/__tests__/*", "**.*.test.ts"],
16 | "ts-node": {
17 | // these options are overrides used only by ts-node
18 | "compilerOptions": {
19 | "module": "CommonJS",
20 | "lib": ["ESNext"],
21 | "types": ["node"],
22 | "target": "ESNext"
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------