• Stars
    star
    763
  • Rank 59,519 (Top 2 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Implement Vercel's useSWR for querying Firestore in React/React Native/Expo apps. 👩‍🚒🔥

SWR + Firestore

const { data } = useDocument('users/fernando')

It's that easy.

🔥 This library provides the hooks you need for querying Firestore, that you can actually use in production, on every screen.

⚡️ It aims to be the fastest way to use Firestore in a React app, both from a developer experience and app performance perspective.

🍕 This library is built on top useSWR, meaning you get all of its awesome benefits out-of-the-box.

You can now fetch, add, and mutate Firestore data with zero boilerplate.

Features

  • Shared state / cache between collection and document queries (instead of Redux??)
  • Works with both React and React Native.
  • Offline mode with Expo (without detaching!)
  • Blazing fast
  • Query collection groups (new in 0.14.x!)
  • set, update, and add update your global cache, instantly
  • TypeScript-ready (see docs)
  • Realtime subscriptions (example)
  • Prevent memory leaks from Firestore subscriptions
  • No more parsing document.data() from Firestore requests
  • Server-side rendering (SSR or SSG) with Next.js (example)
  • Automatic date parsing (no more .toDate())
  • Firebase v8 support (see #59)

...along with the features touted by Vercel's incredible SWR library:

"With SWR, components will get a stream of data updates constantly and automatically. Thus, the UI will be always fast and reactive."

  • Transport and protocol agnostic data fetching
  • Fast page navigation
  • Revalidation on focus
  • Interval polling
  • Request deduplication
  • Local mutation
  • Pagination
  • TypeScript ready
  • SSR support
  • Suspense mode
  • Minimal API

⭐️

If you like this library, give it star and let me know on Twitter!

Installation

yarn add @nandorojo/swr-firestore

# or
npm install @nandorojo/swr-firestore

Install firebase:

# if you're using expo:
expo install firebase

# if you aren't using expo:
yarn add firebase
# or
npm i firebase

Set up

In the root of your app, create an instance of Fuego and pass it to the FuegoProvider.

If you're using Firebase v8, see this solution for creating your instance of Fuego.

If you're using next.js, this goes in your pages/_app.js file.

App.js

import React from 'react'
import 'firebase/firestore'
import 'firebase/auth'
import { Fuego, FuegoProvider } from '@nandorojo/swr-firestore'

const firebaseConfig = {
  // put yours here
}

const fuego = new Fuego(firebaseConfig)

export default function App() {
  return (
    <FuegoProvider fuego={fuego}>
      <YourAppHere />
    </FuegoProvider>
  )
}

Make sure to create your Fuego instance outside of the component. The only argument Fuego takes is your firebase config variable.

Under the hood, this step initializes firebase for you. No need to call firebase.initializeApp.

Basic Usage

Assuming you've already completed the setup...

Subscribe to a document

import React from 'react'
import { useDocument } from '@nandorojo/swr-firestore'
import { Text } from 'react-native'

export default function User() {
  const user = { id: 'Fernando' }
  const { data, update, error } = useDocument(`users/${user.id}`, {
    listen: true,
  })

  if (error) return <Text>Error!</Text>
  if (!data) return <Text>Loading...</Text>

  return <Text>Name: {data.name}</Text>
}

Get a collection

import React from 'react'
import { useCollection } from '@nandorojo/swr-firestore'
import { Text } from 'react-native'

export default function UserList() {
  const { data, update, error } = useCollection(`users`)

  if (error) return <Text>Error!</Text>
  if (!data) return <Text>Loading...</Text>

  return data.map(user => <Text key={user.id}>{user.name}</Text>)
}

useDocument accepts a document path as its first argument here. useCollection works similarly.

Simple examples

Query a users collection:

const { data } = useCollection('users')

Subscribe for real-time updates:

const { data } = useDocument(`users/${user.id}`, { listen: true })

Make a complex collection query:

const { data } = useCollection('users', {
  where: ['name', '==', 'fernando'],
  limit: 10,
  orderBy: ['age', 'desc'],
  listen: true,
})

Pass options from SWR to your document query:

// pass SWR options
const { data } = useDocument('albums/nothing-was-the-same', {
  shouldRetryOnError: false,
  onSuccess: console.log,
  loadingTimeout: 2000,
})

Pass options from SWR to your collection query:

// pass SWR options
const { data } = useCollection(
  'albums',
  {
    listen: true,
    // you can pass multiple where conditions if you want
    where: [
      ['artist', '==', 'Drake'],
      ['year', '==', '2020'],
    ],
  },
  {
    shouldRetryOnError: false,
    onSuccess: console.log,
    loadingTimeout: 2000,
  }
)

Add data to your collection:

const { data, add } = useCollection('albums', {
  where: ['artist', '==', 'Drake'],
})

const onPress = async () => {
  // calling this will automatically update your global cache & Firestore
  const documentId = await add({
    title: 'Dark Lane Demo Tapes',
    artist: 'Drake',
    year: '2020',
  })
}

Set document data:

const { data, set, update } = useDocument('albums/dark-lane-demo-tapes')

const onReleaseAlbum = () => {
  // calling this will automatically update your global cache & Firestore
  set(
    {
      released: true,
    },
    { merge: true }
  )

  // or you could call this:
  update({
    released: true,
  })
}

Use dynamic fields in a request:

If you pass null as the collection or document key, the request won't send.

Once the key is set to a string, the request will send.

Get list of users who have you in their friends list

import { useDoormanUser } from 'react-doorman'

const { uid } = useDoormanUser()
const { data } = useCollection(uid ? 'users' : null, {
  where: ['friends', 'array-contains', uid],
})

Get your favorite song

const me = { id: 'fernando' }

const { data: user } = useDocument<{ favoriteSong: string }>(`users/${me.id}`)

// only send the request once the user.favoriteSong exists!
const { data: song } = useDocument(
  user?.favoriteSong ? `songs/${user.favoriteSong}` : null
)

Parse date fields in your documents

Magically turn any Firestore timestamps into JS date objects! No more .toDate().

Imagine your user document schema looks like this:

type User = {
  name: string
  lastUpdated: {
    date: Date
  }
  createdAt: Date
}

In order to turn createdAt and lastUpdated.date into JS objects, just use the parseDates field:

In a document query

const { data } = useDocument<User>('user/fernando', {
  parseDates: ['createdAt', 'lastUpdated.date'],
})

let createdAt: Date
if (data) {
  // ✅ all good! it's a JS Date now.
  createdAt = data.createdAt
}

data.createdAt and data.lastUpdated.date are both JS dates now!

In a collection query

const { data } = useCollection<User>('user', {
  parseDates: ['createdAt', 'lastUpdated.date'],
})

if (data) {
  data.forEach(document => {
    document.createdAt // JS date!
  })
}

For more explanation on the dates, see issue #4.

Access a document's Firestore snapshot

If you set ignoreFirestoreDocumentSnapshotField to false, you can access the __snapshot field.

const { data } = useDocument('users/fernando', {
  ignoreFirestoreDocumentSnapshotField: false, // default: true
})

if (data) {
  const id = data?.__snapshot.id
}

You can do the same for useCollection and useCollectionGroup. The snapshot will be on each item in the data array.

This comes in handy when you are working with forms for data edits:

With Formik

const { data, set } = useDocument('users/fernando', {
  ignoreFirestoreDocumentSnapshotField: false,
})

if (!data) return <Loading />

<Formik
  initialValues={data.__snapshot.data()}
  ...
/>

With state and hooks

const { data, set } = useDocument('users/fernando', {
  ignoreFirestoreDocumentSnapshotField: false,
})

const [values, setValues] = useState(null);

useEffect(() => {
  if (data) {
    setValues(data.__snapshot.data());
  }
}, [data]);

Paginate a collection:

Video here.

import React from 'react'
import { fuego, useCollection } from '@nandorojo/swr-firestore'

const collection = 'dump'
const limit = 1
const orderBy = 'text'

export default function Paginate() {
  const { data, mutate } = useCollection<{ text: string }>(
    collection,
    {
      limit,
      orderBy,
      // 🚨 this is required to get access to the snapshot!
      ignoreFirestoreDocumentSnapshotField: false,
    },
    {
      // this lets us update the local cache + paginate without interruptions
      revalidateOnFocus: false,
      refreshWhenHidden: false,
      refreshWhenOffline: false,
      refreshInterval: 0,
    }
  )

  const paginate = async () => {
    if (!data?.length) return

    const ref = fuego.db.collection(collection)

    // get the snapshot of last document we have right now in our query
    const startAfterDocument = data[data.length - 1].__snapshot

    // get more documents, after the most recent one we have
    const moreDocs = await ref
      .orderBy(orderBy)
      .startAfter(startAfterDocument)
      .limit(limit)
      .get()
      .then(d => {
        const docs = []
        d.docs.forEach(doc => docs.push({ ...doc.data(), id: doc.id, __snapshot: doc }))
        return docs
      })

    // mutate our local cache, adding the docs we just added
    // set revalidate to false to prevent SWR from revalidating on its own
    mutate(state => [...state, ...moreDocs], false)
  }

  return data ? (
    <div>
      {data.map(({ id, text }) => (
        <div key={id}>{text}</div>
      ))}
      <button onClick={paginate}>paginate</button>
    </div>
  ) : (
    <div>Loading...</div>
  )
}

Query Documents

You'll rely on useDocument to query documents.

import React from 'react'
import { useDocument } from '@nandorojo/swr-firestore'

const user = { id: 'Fernando' }
export default () => {
  const { data, error } = useDocument(`users/${user.id}`)
}

If you want to set up a listener (or, in Firestore-speak, onSnapshot) just set listen to true.

const { data, error } = useDocument(`users/${user.id}`, { listen: true })

API

Imports

import {
  useDocument,
  useCollection,
  useCollectionGroup, // 👋 new!
  revalidateDocument,
  revalidateCollection,
  // these all update BOTH Firestore & the local cache ⚡️
  set, // set a firestore document
  update, // update a firestore document
  fuego, // get the firebase instance used by this lib
  getCollection, // prefetch a collection, without being hooked into SWR or React
  getDocument, // prefetch a document, without being hooked into SWR or React
} from '@nandorojo/swr-firestore'

useDocument(path, options)

const {
  data,
  set,
  update,
  deleteDocument,
  error,
  isValidating,
  mutate,
  unsubscribe
} = useDocument(path, options)

Arguments

  • path required The unique document path for your Firestore document.
    • string | null. If null, the request will not be sent. This is useful if you want to get a user document, but the user ID hasn't loaded yet, for instance.
    • This follows the same pattern as the key argument in useSWR. See the SWR docs for more. Functions are not currently supported for this argument.
  • options (optional) A dictionary with added options for the query. Takes the folowing values:
    • listen = false: If true, sets up a listener for this document that updates whenever it changes.
    • You can also pass any of the options available from useSWR.
    • ignoreFirestoreDocumentSnapshotField = true. See elaboration below.
    • parseDates: An array of string keys that correspond to dates in your document. Example.
ignoreFirestoreDocumentSnapshotField

If true, docs returned in data will not include the firestore __snapshot field. If false, it will include a __snapshot field. This lets you access the document snapshot, but makes the document not JSON serializable.

By default, it ignores the __snapshot field. This makes it easier for newcomers to use JSON.stringify without weird errors. You must explicitly set it to false to use it.

// include the firestore document snapshots
const { data } = useDocument('users/fernando', {
  ignoreFirestoreDocumentSnapshotField: false,
})

if (data) {
  const path = data.__snapshot.ref.path
}

The __snapshot field is the exact snapshot returned by Firestore.

See Firestore's snapshot docs for more.

Return values

Returns a dictionary with the following values:

  • set(data, SetOptions?): Extends the firestore document set function.
    • You can call this when you want to edit your document.
    • It also updates the local cache using SWR's mutate. This will prove highly convenient over the regular Firestore set function.
    • The second argument is the same as the second argument for Firestore set.
  • update(data): Extends the Firestore document update function.
    • It also updates the local cache using SWR's mutate. This will prove highly convenient over the regular set function.
  • deleteDocument(): Extends the Firestore document delete function.
    • It also updates the local cache using SWR's mutate by deleting your document from this query and all collection queries that have fetched this document. This will prove highly convenient over the regular delete function from Firestore.
  • unsubscribe() A function that, when called, unsubscribes the Firestore listener.
    • The function can be null, so make sure to check that it exists before calling it.
    • Note: This is not necessary to use. useDocument already unmounts the listener for you. This is only intended if you want to unsubscribe on your own.

The dictionary also includes the following from useSWR:

  • data: data for the given key resolved by fetcher (or undefined if not loaded)
  • error: error thrown by fetcher (or undefined)
  • isValidating: if there's a request or revalidation loading
  • mutate(data?, shouldRevalidate?): function to mutate the cached data

useCollection(path, query, options)

const { data, add, error, isValidating, mutate, unsubscribe } = useCollection(
  path,
  query,
  options
)

Arguments

  • path required string, path to collection.
  • query optional dictionary with Firestore query details
  • options SWR options (see SWR docs)

path

path required The unique document path for your Firestore document.

  • string | null. If null, the request will not be sent. This is useful if you want to get a user document, but the user ID hasn't loaded yet, for instance.
  • This follows the same pattern as the key argument in useSWR. See the SWR docs for more. Functions are not currently supported for this argument.

query

(optional) Dictionary that accepts any of the following optional values:

  • listen = false: if true, will set up a real-time listener that automatically updates.
  • limit: number that limits the number of documents
  • where: filter documents by certain conditions based on their fields
  • orderBy: sort documents by their fields
  • startAt: number to start at
  • endAt: number to end at
  • startAfter: number to start after
  • endBefore: number to end before
  • ignoreFirestoreDocumentSnapshotField = true: If true, docs returned in data will not include the firestore __snapshot field. If false, it will include a __snapshot field. This lets you access the document snapshot, but makes the document not JSON serializable.
where

Can be an array, or an array of arrays.

Each array follows this outline: ['key', 'comparison-operator', 'value']. This is pulled directly from Firestore's where pattern.

// get all users whose names are Fernando
useCollection('users', {
  where: ['name', '==', 'Fernando'],
})

// get all users whose names are Fernando & who are hungry
useCollection('users', {
  where: [
    ['name', '==', 'Fernando'],
    ['isHungry', '==', true],
  ],
})

// get all users whose friends array contains Fernando
useCollection('users', {
  where: ['friends', 'array-contains', 'Fernando'],
})
orderBy

Can be a string, array, or an array of arrays.

Each array follows this outline: ['key', 'desc' | 'asc']. This is pulled directly from Firestore's orderBy pattern.

// get users, ordered by name
useCollection('users', {
  orderBy: 'name',
})

// get users, ordered by name in descending order
useCollection('users', {
  orderBy: ['name', 'desc'],
})

// get users, ordered by name in descending order & hunger in ascending order
useCollection('users', {
  orderBy: [
    ['name', 'desc'], //
    ['isHungry', 'asc'],
  ],
})
ignoreFirestoreDocumentSnapshotField

If true, docs returned in data will not include the firestore __snapshot field. If false, it will include a __snapshot field. This lets you access the document snapshot, but makes the document not JSON serializable.

By default, it ignores the __snapshot field. This makes it easier for newcomers to use JSON.stringify without weird errors. You must explicitly set it to false to use it.

// include the firestore document snapshots
const { data } = useCollection('users', {
  ignoreFirestoreDocumentSnapshotField: false,
})

if (data) {
  data.forEach(document => {
    const path = document?.__snapshot.ref.path
  })
}

The __snapshot field is the exact snapshot returned by Firestore.

See Firestore's snapshot docs for more.

options

(optional) A dictionary with added options for the request. See the options available from SWR.

Return values

Returns a dictionary with the following values:

  • add(data): Extends the Firestore document add function. Returns the added document ID(s).
    • It also updates the local cache using SWR's mutate. This will prove highly convenient over the regular add function provided by Firestore.

The returned dictionary also includes the following from useSWR:

  • data: data for the given key resolved by fetcher (or undefined if not loaded)
  • error: error thrown by fetcher (or undefined)
  • isValidating: if there's a request or revalidation loading
  • mutate(data?, shouldRevalidate?): function to mutate the cached data
  • unsubscribe() A function that, when called, unsubscribes the Firestore listener.
    • The function can be null, so make sure to check that it exists before calling it.
    • Note: This is not necessary to use. useCollection already unmounts the listener for you. This is only intended if you want to unsubscribe on your own.

useCollectionGroup(path, query, options)

Follows an identical API as useCollection, except that it leverages Firestore's collection group query for merging subcollections with the same name.

To see how to use it, follow the instructions from useCollection.

See the Firestore docs on collecttion groups to learn more.

set(path, data, SetOptions?)

Extends the firestore document set function.

  • You can call this when you want to edit your document.
  • It also updates the local cache using SWR's mutate. This will prove highly convenient over the regular Firestore set function.
  • The second argument is the same as the second argument for Firestore set.

This is useful if you want to set a document in a component that isn't connected to the useDocument hook.

update(path, data):

Extends the Firestore document update function.

  • It also updates the local cache using SWR's mutate. This will prove highly convenient over the regular set function.

This is useful if you want to update a document in a component that isn't connected to the useDocument hook.

deleteDocument(path, ignoreLocalMutations = false)

Extends the Firestore document delete function.

  • It also updates the local cache using SWR's mutate by deleting your document from this query and all collection queries that have fetched this document. This will prove highly convenient over the regular delete function from Firestore.
  • Second argument is a boolean that defaults to false. If true, it will not update the local cache, and instead only send delete to Firestore.

revalidateDocument(path)

Refetch a document from Firestore, and update the local cache. Useful if you want to update a given document without calling the connected revalidate function from use useDocument hook.

  • Only argument is the Firestore document path (ex: users/Fernando)

revalidateCollection(path)

Refetch a collection query from Firestore, and update the local cache. Useful if you want to update a given collection without calling the connected revalidate function from use useCollection hook.

  • Only argument is the Firestore document path (ex: users)
  • Note Calling revalidateCollection will update all collection queries. If you're paginating data for a given collection, you probably won't want to use this function for that collection.

fuego

The current firebase instance used by this library. Exports the following fields:

  • db: the current firestore collection instance
  • auth: the firebase.auth variable.
import { fuego } from '@nandorojo/swr-firestore'

fuego.db.doc('users/Fernando').get()

fuego.auth().currentUser?.uid

getDocument(path, options?)

If you don't want to use useDocument in a component, you can use this function outside of the React scope.

Arguments

  • path required The unique document path for your Firestore document.
  • options
    • ignoreFirestoreDocumentSnapshotField = true. If false, it will return a __snapshot field too.
    • parseDates: An array of string keys that correspond to dates in your document. Example.

Returns

A promise with the firestore doc and some useful fields. See the useDocument data return type for more info.

getCollection(path, query?, options?)

If you don't want to use useCollection in a component, you can use this function outside of the React scope.

Arguments

  • path required The unique collection path for your Firestore collection.
    • ignoreFirestoreDocumentSnapshotField = true. If false, it will return a __snapshot field too.
    • parseDates: An array of string keys that correspond to dates in your document. Example.
  • query refer to the second argument of useCollection.
  • options
    • ignoreFirestoreDocumentSnapshotField = true. If false, it will return a __snapshot field too in each document.
    • parseDates: An array of string keys that correspond to dates in your documents. Example.

Features

TypeScript Support

Create a model for your typescript types, and pass it as a generic to useDocument or useCollection.

useDocument

The data item will include your TypeScript model (or null), and will also include an id string, an exists boolean, and hasPendingWrites boolean.

type User = {
  name: string
}

const { data } = useDocument<User>('users/fernando')

if (data) {
  const {
    id, // string
    name, // string
    exists, // boolean
    hasPendingWrites, // boolean
  } = data
}

const id = data?.id //  string | undefined
const name = data?.name // string | undefined
const exists = data?.exists // boolean | undefined
const hasPendingWrites = data?.hasPendingWrites // boolean | undefind

useCollection

The data item will include your TypeScript model (or null), and will also include an id string.

type User = {
  name: string
}

const { data } = useCollection<User>('users')

if (data) {
  data.forEach(({ id, name }) => {
    // ...
  })
}

Shared global state between documents and collections

A great feature of this library is shared data between documents and collections. Until now, this could only be achieved with something like a verbose Redux set up.

So, what does this mean exactly?

Simply put, any documents pulled from a Firestore request will update the global cache.

To make it clear, let's look at an example.

Imagine you query a user document from Firestore:

const { data } = useDocument('users/fernando')

And pretend that this document's data returns the following:

{ "id": "fernando", "isHungry": false }

Remember that isHungry is false here ^

Now, let's say you query the users collection anywhere else in your app:

const { data } = useCollection('users')

And pretend that this collection's data returns the following:

[
  { "id": "fernando", "isHungry": true },
  {
    //...
  }
]

Whoa, isHungry is now true. But what happens to the original document query? Will we have stale data?

Answer: It will automatically re-render with the new data!

swr-firestore uses document id fields to sync any collection queries with existing document queries across your app.

That means that if you somehow fetch the same document twice, the latest version will update everywhere.

License

MIT

More Repositories

1

moti

🐼 The React Native (+ Web) animation library, powered by Reanimated 3.
TypeScript
3,956
star
2

solito

🧍‍♂️ React Native + Next.js, unified.
TypeScript
3,426
star
3

dripsy

🍷 Responsive, unstyled UI primitives for React Native + Web.
TypeScript
1,918
star
4

zeego

Menus for React (Native) done right.
TypeScript
1,520
star
5

burnt

Crunchy toasts for React Native. 🍞
Java
1,140
star
6

expo-next-react-navigation

⛴ Make Next.js and react-navigation play nicely together with an Expo/React Native Web app.
TypeScript
388
star
7

react-navigation-heavy-screen

⚡️Optimize heavy screens to prevent lags during React Navigation transitions.
TypeScript
320
star
8

react-native-anchors

🦅 Anchor links and scroll-to utilities for React Native (+ Web)
JavaScript
299
star
9

swr-react-native

React Native/React Navigation compatibility for Vercel's useSWR hook. 🐮
TypeScript
285
star
10

galeria

The React (Native) Image Viewer. 📷
TypeScript
169
star
11

expo-next-monorepo

💸 Expo + Next.js monorepo starter.
JavaScript
114
star
12

expo-spotify-party

🎹 Listen to Spotify with your friends in real-time. (React Native Expo + Next.js)
TypeScript
88
star
13

nextjs-conf-22-example

Example repository for the 2022 Next.js Conf about React Native.
TypeScript
67
star
14

react-native-iconic

🎨 Iconic icons for React Native (+Web)
TypeScript
65
star
15

react-native-heroicons

Hero Icons for React Native. 🫡
TypeScript
64
star
16

zero-to-10m

Resources from my talk at Next.js Conf: "Zero to $10 Million with React Native + Next.js"
57
star
17

expo-firestore-offline-persistence

❄️ Enable Firestore offline persistence in Expo apps without detaching.
JavaScript
51
star
18

tamagui-intellisense

VSCode Plugin for Tamagui
JavaScript
41
star
19

sf-symbols-typescript

TypeScript types for iOS SF Symbols 🍏
TypeScript
36
star
20

doorman

🚪Doorman is a full-stack phone authentication solution for Expo + Firebase auth.
TypeScript
29
star
21

reanimated-tree-shaking

A Next.js playground to test Reanimated bundle size.
TypeScript
24
star
22

sticky

Implement position: sticky in React Native
TypeScript
15
star
23

ts-monorepo-autoimport-guide

A guide for making VSCode's autoimport work in a monorepo.
12
star
24

expo-gatsby-navigation

🤵Make Gatsby and React Navigation play nicely together with an Expo/React Native Web app.
TypeScript
12
star
25

tgui

TypeScript
11
star
26

react-native-2030

The implementation of my App.js Conf talk, "React Native 2030"
11
star
27

fuego

🔥Firebase Firestore hooks & components for React Native/React.
TypeScript
10
star
28

shared-animations

🕺A global state manager for React Native animations.
JavaScript
10
star
29

expo-electron-copy-magic

Copy clip sucks, so this is a better one made with Expo + Electron.
TypeScript
10
star
30

lint-expo

🐠 A mini script that configures prettier and eslint for React/Expo projects.
JavaScript
9
star
31

tamagui-starter

Starter project for issue reproductions on CodeSandbox
TypeScript
8
star
32

doorman-examples

🥳A repository of example apps for Doorman (Expo + Firebase phone auth)
TypeScript
8
star
33

solito-test

TypeScript
6
star
34

react-doorman

React Core library for Doorman's Firebase Auth, without the RN dependencies. 👾Meant for use in Gatsby/other React apps.
TypeScript
4
star
35

expo-eas-semantic-release

git push → automated Expo EAS builds, over-the-air updates, & prerelease versions.
JavaScript
4
star
36

lexical-ios-react-native

Just an experiment with Expo Modules
Swift
4
star
37

expo-starter-project

eslint, src folder, etc
TypeScript
3
star
38

panda

🐼 Design websites in seconds. Export clean code. (PRE-ALPHA)
JavaScript
3
star
39

expo-navigation-core

Package with reusable expo web navigation hooks / components.
TypeScript
2
star
40

lint-react-native-app

🤓 Simple command to add ESLint and Prettier to a react-native project.
TypeScript
2
star
41

thet

TypeScript
2
star
42

animateMePlz

⚡️ A lightweight jQuery extension for scrolling animations.
HTML
2
star
43

expo-router-bug

TypeScript
1
star
44

nandorojo

1
star
45

plaid-bug-repro

Repro for https://github.com/plaid/react-native-plaid-link-sdk/issues/436
Objective-C
1
star
46

expo-next-issue

⚡️React-native-paper issue with expo / next.
JavaScript
1
star
47

react-native-bootstrap

React-bootstrap, remade for React Native (& React Native For Web).
TypeScript
1
star
48

reanimated-next13-issue

Reanimated isn't working with Next.js 13.
JavaScript
1
star
49

react-native-beacons-example

Repo for react-native-beacons
TypeScript
1
star
50

react-spring-next-issue

Reproducible repo for this issue: https://github.com/react-spring/react-spring/issues/627#issuecomment-541235141
TypeScript
1
star
51

next-style-issue

Issue: prop did not match, server: x, client: y for issue: https://github.com/vercel/next.js/discussions/14469
TypeScript
1
star
52

writing-javascript-actions

1
star
53

eslint-config-nando

Stress-free react native linting. 🏋🏻‍♂️
JavaScript
1
star
54

Penn-Requirement-Counter

🤓 Find courses that count for one or more requirements at UPenn.
JavaScript
1
star
55

expo-web-img-picker-bug

SDK 41 image picker not working on web.
JavaScript
1
star
56

expo-next-webpack5-issue

Reproduction of Webpack 5 not working with Expo in a Next.js app
JavaScript
1
star