rslabdocs

Getting started

Install RSLAB animations in your Expo app.

RSLAB is a component library for Expo and React Native. Every animation ships as source code, written with Reanimated, Skia, Gesture Handler and WebGPU. You install it, you own it, you tune it.

Requirements

  • An Expo app on SDK 57 or newer
  • react-native-reanimated and react-native-gesture-handler, with your root layout wrapped in GestureHandlerRootView
  • The NativeWind v5 runtime for className support, see One-time setup below
  • Some animations also need @shopify/react-native-skia or react-native-webgpu, each entry lists its stack in the registry

1. Subscribe

Every entry in the registry, components and animations alike, is a paid subscription, there's no free tier. Pick a plan first.

2. Log the CLI in

npx @rslab/cli login

This opens a browser link, sign in, click Authorize, and the CLI is connected. No token to copy anywhere.

3. Add a component

npx @rslab/cli add pull-search

The CLI writes the component into your project, tells you which dependencies are missing, and never touches anything else. It works with npm, pnpm, yarn and bun. The code is yours after that, edit it freely. If your app keeps its code in src/ (the default template does), the CLI detects it and writes there.

One-time setup

Every component styles itself through tw, a set of className-enabled primitives built on NativeWind v5 and react-native-css. A fresh Expo app needs three small config files once, then every RSLAB install just works. The snippets below are everything RSLAB needs, the NativeWind docs go deeper if you want the full picture.

Install the runtime:

npx expo install nativewind@preview tailwindcss @tailwindcss/postcss react-native-css

The @preview tag matters: npm's latest for NativeWind is still the v4 line, and v4's Metro helper has a different name, so a plain nativewind install ends in withNativewind is not a function. rslab doctor catches this if it happens.

Then pin lightningcss in your package.json (yarn users: resolutions instead of overrides) and reinstall, newer lightningcss builds break Tailwind's css parsing with a failed to deserialize; expected an object-like struct named Specifier error at bundle time:

package.json
{
  "overrides": {
    "lightningcss": "1.30.1"
  }
}

Wrap Metro in metro.config.js:

metro.config.js
const { getDefaultConfig } = require('expo/metro-config');
const { withNativewind } = require('nativewind/metro');

module.exports = withNativewind(getDefaultConfig(__dirname), {
  inlineVariables: false,
  globalClassNamePolyfill: false,
});

Point PostCSS at Tailwind in postcss.config.mjs:

postcss.config.mjs
export default {
  plugins: {
    '@tailwindcss/postcss': {},
  },
};

And add the Tailwind imports at the top of the CSS file your root layout already imports (src/global.css in the default template):

global.css
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/preflight.css' layer(base);
@import 'tailwindcss/utilities.css';

Finally, gesture-driven components need the usual GestureHandlerRootView around your root layout:

app/_layout.tsx
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function RootLayout() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      {/* your existing layout */}
    </GestureHandlerRootView>
  );
}

Restart Metro with npx expo start --clear after the config change, and you're set for every install after that.

WebGPU animations

One entry, motion-fossil, renders through three.js on WebGPU. It needs a custom development build with react-native-webgpu (it can't run in Expo Go), and two Metro resolution rules on top of the base config above, otherwise @react-three/fiber's CommonJS build tries to pull expo-gl and the bundle fails:

metro.config.js
config.resolver.resolveRequest = (context, moduleName, platform) => {
  // three's WebGPU renderer lives in a subpath
  if (moduleName.startsWith('three')) {
    moduleName = 'three/webgpu';
  }
  // force fiber's ESM build on native, its CJS build requires expo-gl
  if (platform !== 'web' && moduleName.startsWith('@react-three/fiber')) {
    return context.resolveRequest(
      {
        ...context,
        unstable_conditionNames: ['module'],
        mainFields: ['module'],
      },
      moduleName,
      platform,
    );
  }
  return context.resolveRequest(context, moduleName, platform);
};

Skip this entirely if you don't install motion-fossil, the base config is enough for everything else.

Check your setup

Not sure you got everything? The CLI checks all of it for you:

npx @rslab/cli doctor

Five checks, one line each, and every ✗ comes with the exact command or file to fix. The same checker is exposed to AI agents as the check_setup tool on the MCP server, so your agent can verify a project before it installs anything.

CI and teammates

login opens a browser, which doesn't exist in CI. Grab a token from the CLI access panel on your account page instead, and drop it in the project's .env:

RSLAB_TOKEN=rs_live_xxxxxxxxxxxxxxxxxxxx

The next add picks it up automatically, no login needed on that machine. You can also pass it inline, which is handy for a one-off CI step:

RSLAB_TOKEN=rs_live_xxxxxxxxxxxxxxxxxxxx npx @rslab/cli add pull-search

Treat this token like a password, anyone with it can install every paid item in the registry. Add .env to your .gitignore if it isn't already there.

Coming from the lab

Every animation starts as an experiment on rselmi.com/lab, with a full breakdown of how it works. The registry versions are the polished, props-driven builds, and new drops ship regularly, subscribers propose and vote on what's next.

On this page