Solito

repository·master·Indexed 23 days ago

https://github.com/nandorojo/solito

A library that unifies React Native and Next.js, enabling developers to share code between mobile (via React Native/Expo) and web (via Next.js) applications with a focus on unified navigation. It includes the create-solito-app CLI for scaffolding monorepos and provides navigation hooks like useRouter, useParams, and usePathname for the Next.js App Router.

Tokens
30.6K
Snippets
99
Records
168
Agent score
86%

What's inside solito

  1. Overview of Solito

    master
    Solito is a library designed to unify React Native with Next.js, with a primary focus on providing a unified navigation experience across both platforms. It serves as a next-generation successor to expo-next-react-navigation, featuring a brand new API and architectural approach.
  2. Understand Solito core concepts

    master

    Solito is a library designed to bridge React Native and Next.js for cross-platform application development. It provides two primary capabilities:

    1. Shared Navigation: A lightweight wrapper around react-navigation and next/router that allows you to write navigation logic once and share it across both native (Expo/React Native) and web (Next.js) platforms.
    2. Cross-platform Patterns: A collection of architectural patterns and examples specifically for building apps using the React Native + Next.js stack.
  3. Understand Solito's platform-specific navigation approach

    master

    Solito avoids the complexity of mixing navigation libraries by letting each platform use its native standard. It ensures that code for one platform is never imported into the other:

    • On Web: Solito uses next/router and next/link.
    • On Native (iOS/Android): Solito uses React Navigation.

    This approach prevents the 'two sources of truth' problem found in other unification attempts and keeps bundle sizes optimized by ensuring React Navigation is never imported on Web, and Next.js code is never imported on Native.

  4. Understand the Solito monorepo folder layout

    master

    The Solito starter uses an opinionated folder structure to separate platform entry points from shared logic.

    • apps/: Contains the entry points for each platform.
      • expo/: The React Native / Expo application.
      • next/: The Next.js web application.
    • packages/: Contains shared code used by both apps.
      • app/: The primary location for shared logic. Most files should be imported from here.
        • features/: Organize code by feature here (avoid using a screens folder).
        • provider/: Contains app providers and web-specific no-ops.
        • navigation/: Contains navigation-related code for React Native (since RN lacks a pages/ directory like Next.js).
  5. Understand the Solito Tailwind monorepo layout

    master

    The project is organized into apps (entry points) and packages (shared code):

    • apps/expo/app: File system routing for iOS and Android.
    • apps/next: Web entry point.
    • packages/app/features: Shared business logic and features (organized by feature, not by screens).
    • packages/app/provider: App-wide providers (including no-ops for Web).
    • packages/app/design: The design system, including typography and layout components.
  6. Implement Next.js route modals with Solito

    master

    In Next.js, you can use shallow routing to open a page as a modal. This allows you to change the URL and render a component on top of the current view without unmounting the background content (preserving scroll position and local state).

    1. Use router.push with the shallow: true option.
    2. Use the as parameter (the second argument in router.push) to define the clean URL for the user.
    3. In the page component, use Solito's createParam to detect the presence of the parameter and conditionally render the Modal.

    On Native, Solito will interpret the as path and open the corresponding screen in a navigation stack.

    // 1. In your shared feature component:
    export function SearchList() {
      const router = useRouter()
      return (
        <ArtistResults
          onPressArtist={(artist) => {
            // Use shallow routing to open the artist as a modal
            router.push(`/search?artistSlug=${artist.slug}`, `/@${artist.slug}`, {
              shallow: true,
            })
          }}
        />
      )
    }
    
    // 2. In your Next.js page component:
    import { createParam } from 'solito'
    import { Modal } from 'react-native'
    import dynamic from 'next/dynamic'
    const ArtistPage = dynamic(() => import('../artist/[artistSlug]'))
    
    const { useParam } = createParam<{ artistSlug?: string }>()
    
    export default function SearchPage() {
      const [artistSlug] = useParam('artistSlug')
    
      return (
        <>
          <SearchList />
          <Modal visible={Boolean(artistSlug)}>
            <ArtistPage />
          </Modal>
        </>
      )
    }
  7. Decide if Solito is right for your project

    master

    Solito is recommended in the following scenarios:

    1. Building a Next.js app: It provides standard functionality (similar to useRouter and Link) plus utilities like useParam, while ensuring any native-only code is tree-shaken.
    2. Building a React Native app with React Navigation: Using Solito's URL-based mental model for navigation and parameter reading makes it significantly easier to spin up a companion Next.js website later.
    3. Future-proofing: If you anticipate ever needing to support React Native or React Native Web, Solito provides the glue to manage cross-platform code sharing.
  8. Deploy Expo updates via GitHub Actions in a monorepo

    master

    To automate mobile app updates using Expo's GitHub Actions in a Solito monorepo, you must specify the working-directory in your workflow file. This ensures the eas update command runs from the correct directory (typically ./apps/expo) rather than the monorepo root.

    - name: Publish update
      run: eas update --auto
      working-directory: ./apps/expo
  9. Integrate MotiPressable with Solito's useLink hook

    master

    To create animated, accessible links using moti's MotiPressable component, you should use Solito's useLink hook instead of the standard <Link /> component. This allows you to spread the link properties (like href and as) directly onto the MotiPressable component, enabling animations based on hover and pressed states while maintaining navigation functionality.

    import { MotiPressable } from 'moti/interactions'
    import { useLink, UseLinkProps } from 'solito/link'
    
    type MotiLinkProps = UseLinkProps & 
      Omit<React.ComponentProps<typeof MotiPressable>, keyof UseLinkProps>
    
    export function MotiLink({
      as,
      href,
      shallow,
      children,
      ...motiPressableProps
    }: MotiLinkProps) {
      const linkProps = useLink({
        as,
        href,
        shallow,
      })
    
      return (
        <MotiPressable {...motiPressableProps} {...linkProps}>
          {children}
        </MotiPressable>
      )
    }
  10. Use MotiLink for animated links

    master

    Solito provides MotiLink for first-class integration with the moti animation library. It behaves like a standard Link (showing URLs in browsers and supporting Command + Click) but allows you to use Moti's animation features. It is built on top of MotiPressable and supports animations based on hovered and pressed states using Reanimated worklets, which avoids unnecessary React re-renders.

    import { MotiLink } from 'solito/moti'
    
    // Example: Animating scale on hover and press
    const ButtonLink = ({ children, href, as }) => (
      <MotiLink
        href={href}
        as={as}
        animate={({ hovered, pressed }) => {
          'worklet'
    
          return {
            scale: pressed ? 0.9 : hovered ? 1.1 : 1,
          }
        }}
      >
        {children}
      </MotiLink>
    )
    
    // Example: Simple fade-in animation
    <MotiLink href="/users/fernando" from={{ opacity: 0 }} animate={{ opacity: 1 }}>
      {children}
    </MotiLink>
  11. Understand the Solito Methodology and Code Sharing

    master

    Solito uses a "headless navigation" approach where URLs are the single source of truth. This allows you to share 100% of your screen logic and navigation logic across Web and Native platforms while allowing each platform to handle the actual rendering (the "skeleton") differently.

    What is shared:

    • Screen components: The logic and UI primitives (e.g., <ArtistScreen />) are identical across iOS, Android, and Web.
    • Navigation logic: The code used to navigate between screens is shared.

    What is platform-specific:

    • Rendering/Implementation: How screens are displayed.
      • Native (iOS/Android): Uses React Navigation (stacks, tabs, drawers) to render shared screens.
      • Web (Next.js): Renders shared screens inside the pages directory (e.g., /pages/artist/[slug]).

    How it works:

    Solito maps URLs to screens. When you use a component like <Link href="/users/fernando" />, Solito detects the platform and determines whether to trigger a Next.js route change or a React Navigation transition. The platforms (Next.js and React Native) remain isolated and do not communicate with each other directly.