Migrating an existing project from SpiroKit v1 to v2

Migrating an existing project from SpiroKit v1 to v2

ยท

9 min read

This document is a work in progress. I'll keep updating this in case I find an edge case I forget to document.

In this article, I'll describe the required steps to migrate an existing SpiroKit v1 project so you can use v2, which comes with an entire rewrite and uses Tamagui's compiler. If you want to learn more about v2, please check out this article.

If you are just starting a new project with SpiroKit, you can already use our two starter templates for v2:

# Starter with Expo SDK 49, Expo Router v2, TypeScript and SpiroKit v2
npx create-spirokit-app@latest --template expo-template-typescript-v2

# Web Starter with NextJS 13, TypeScript and SpiroKit v2
npx create-spirokit-app@latest --template nextjs-template-typescript-v2

Package.json

On v1, the package you installed from the private repo was called @spirokit/core. On v2, you need to remove that package, and add the following packages:

Besides, SpiroKit is now using @tamagui/lucide-icons instead of react-native-heroicons (More info about how to replace the items below)

Here's an example of your package.json after the changes

{
    "name": "expo-example",
    "version": "1.0.0",
    "main": "index.js",
    "scripts": {
-        "start": "expo start",
+       "start": "TAMAGUI_TARGET=native expo start",
-        "android": "expo start --android",
+       "android": "TAMAGUI_TARGET=native expo start --android",
-        "ios": "expo start --ios",
+       "ios": "TAMAGUI_TARGET=native expo start --ios",
-        "web": "expo start --web",
+       "web": "TAMAGUI_TARGET=web expo start --web",
    },
    "dependencies": {
-        "@spirokit/core": "latest",
+       "@spirokit/ui": "next",
+       "@spirokit/native": "next",
+       "@shopify/flash-list": "^1.4.3",
        "@expo-google-fonts/poppins": "^0.2.2",
-       "@expo/webpack-config": "^0.17.2",
-       "expo": "^47.0.0",
+       "expo": "^49.0.0",
-       "expo-linear-gradient": "~12.0.1",
        "expo-status-bar": "~1.4.2",
        "react": "18.1.0",
        "react-dom": "18.1.0",
        "react-native": "0.70.5",
-       "react-native-heroicons": "2.0.2",
+       "@tamagui/lucide-icons": "1.40.0",
        "react-native-safe-area-context": "4.4.1",
        "react-native-svg": "13.4.0",
        "react-native-web": "~0.18.7"
    },
    "devDependencies": {
        "@babel/core": "^7.19.3",
        "@types/react": "~18.0.24",
        "@types/react-native": "~0.70.6",
        "typescript": "^4.6.3"
    },
    "private": true
}

If you can, start from scratch using our new templates mentioned above, and then copy / paste your code.

If you want to keep your current project, check the package.json from our v1 and v2 templates


Upgrading imports

Search for from "@spirokit/core" and replace all the occurrences for from "@spirokit/ui".

You may get errors on missing components like DateTimePicker and Pressable. More info about this below.


Tokens

One big change from v1 to v2, is that all the design tokens are now strings, and are pre-pended with a "$" sign. This is how Tamagui works. Let me show you an example:

// v1
return (
  <Box padding={4} marginTop={2}>
    <Body fontWeight="bold">Hello world</Body>
  </Box>
)

// v2
return (
  <Box padding="$4" marginTop="$2">
    <Body fontWeight="$bold">Hello world</Body>
  </Box>
)

On v1, you usually use numbers to set things like padding, margin, borderRadius, etc. But there were "special values" defined on this list that were mapped to other values:

  • Use 4 to get 16px

  • Use 6 to get 24px

That was convenient but also confusing: If you were using 32, you would get 128px, but if you used 33, you get 33px.

On v2, you can still use numeric values, but all the size tokens now are strings with the "$" prefix.

After migrating a few apps to v2, I came up with a list of things you may need to check and replace. Thankfully, VSCode provides an excellent tool to find and replace these tokens quickly.

Font tokens

  • fontWeight

    • Search for fontWeight=

    • Add the "$" prefix. Here are the values

      • <Body fontWeight="bold" /> -> <Body fontWeight="$bold" />
  • fontSize

    • Search for fontSize=

    • Add the "$" prefix. Here are the values

      • <Body fontSize="lg" /> -> <Body fontSize="$lg" />
  • lineHeight

    • Search for lineHeight=

    • Add the "$" prefix. Here are the values

      • <Body lineHeight="xl" /> -> <Body lineHeight="$xl" />
  • letterSpacing

    • Search for letterSpacing=

    • Add the "$" prefix. Here are the values

      • <Body letterSpacing="xl" /> -> <Body letterSpacing="$xl" />

Color tokens

  • backgroundColor, borderColor, color, shadowColor, outlineColor

    • Search for backgroundColor=, borderColor=, shadowColor, outlineColor

    • Add the "$" prefix

      • <Box backgroundColor="primary.500" /> -> <Box backgroundColor="$primary.500" />

Size tokens

  • margin, marginLeft, marginRight, marginTop, marginBottom

    • Search for margin={, marginLeft={, marginRight={, marginTop={, marginBottom={

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box margin={4} /> -> <Box margin="$4" />

      • <Box marginTop={4} /> -> <Box marginTop="$4" />

  • padding, paddingLeft, paddingRight, paddingTop, paddingBottom

    • Search for padding={, paddingLeft={, paddingRight={, paddingTop={, paddingBottom={

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box padding={4} /> -> <Box padding="$4" />

      • <Box paddingBottom={4} /> -> <Box paddingBottom="$4" />

  • marginX

    • Search for marginX={

    • On v2, it was renamed to use marginHorizontal instead

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box marginX={4} /> -> <Box marginHorizontal="$4" />
  • marginY

    • Search for marginY={

    • On v2, it was renamed to use marginVertical instead

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box marginY={4} /> -> <Box marginVertical="$4" />
  • paddingX

    • Search for paddingX={

    • On v2, it was renamed to use paddingHorizontal instead

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box paddingX={4} /> -> <Box paddingHorizontal="$4" />
  • paddingY

    • Search for paddingY={

    • On v2, it was renamed to use paddingVertical instead

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box paddingY={4} /> -> <Box paddingVertical="$4" />
  • space

    • Search for space={

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box space={4} /> -> <Box space="$4" />
  • width, minWidth, maxWidth

    • Search for width={, minWidth={, maxWidth={

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box width={4} /> -> <Box width="$4" />

      • <Box width={"full"} /> -> <Box width="$full" />

  • height, minHeight, maxHeight

    • Search for height={, minHeight={, maxHeight={

    • Search for values in the size scale, like 1 to 10, 12,16,20,24, etc. Replace those values with strings with the "$" prefix. Full list of values

      • <Box height={4} /> -> <Box height="$4" />

      • <Box height={"full"} /> -> <Box height="$full" />


Border-radius tokens

On v1, border-radius allowed numeric values and the following tokens:

{
    "none": 0,
    "xs": 2,
    "sm": 4,
    "md": 6,
    "lg": 8,
    "xl": 12,
    "2xl": 16,
    "3xl": 24,
    "full": 9999
}

On v2, you can still use numeric values, and the following tokens (as string, with the "$" prefix:

{
  0: 0,
  1: 3,
  2: 5,
  3: 7,
  4: 9,
  true: 9,
  5: 10,
  6: 16,
  7: 19,
  8: 22,
  9: 26,
  10: 34,
  11: 42,
  12: 50,
}

You can use the new tokens like this

  • <Box borderRadius="2xl"/> -> <Box borderRadius="$6"

  • <Box borderRadius="full"/> -> <Box borderRadius="$12"

Or you can replace the old tokens with the numeric values

  • <Box borderRadius="2xl"/> -> <Box borderRadius={16}

Pseudo props

SpiroKit v1 was built on top of NativeBase. That's why you could use pseudo props like:

  • _hover

  • _pressed

  • _focus

  • Platform-specific

    • _web

    • _iOS

    • _android

On SpiroKit v2, we are inheriting pseudo props from Tamagui. Platform-specific props are not available yet, so you'll need to check for the platform and conditionally add styles for now.

But you can still use the hover, pressed, and focus props with a new name.

  • _hover -> _hoverStyle

  • _pressed -> _pressStyle

  • _focus -> _focusStyle

Learn more about these props here


Media props

On v1, you were able to set responsive values like this:

<Box width={{ base: "full", lg: "1/2" }}/>

On v2, we have separated props for each breakpoint:

<Box width="$full" $gtLg="$1/2" />

Breaking changes in components

  • The Pressable component was removed. Now all the common layout components like Box, VStack, HStack, etc already include a onPress prop.

  • Button component

    • The size prop no longer include the value "xs". use "sm" instead.

      You may notice that the buttons are a little bit smaller than in v1. This was based on feedback from users.

  • Badge and FlatList components

    • On v1, you could add style props like this:

        <Badge paddingX={4} paddingY={2}>Hello</Badge>
      
    • On v2, style props were moved into the _container prop

        <Badge _container={{ 
          paddingHorizontal: "$4", 
          paddingVertical: "$2"
        }}>Hello</Badge>
      
  • Switch component

    • value prop was replaced with the checked prop.
  • Use DateTimeInput instead of DateTimePicker

    • The DatePicker component was temporarily removed. v1 was built on top of react-native-modern-datepicker and it had performance issues.

    • I'll rebuild this component soon. In the meantime, you can use the new DateTimeInput component. Documentation with examples will be available soon.

  • Select component

    • ItemComponent prop was renamed. Use renderItem instead.

      This component is now using FlashList by Shopify, so enjoy the new performance boost ๐Ÿ˜‰

  • Box, HStack, VStack, etc

    • LinearGradient is not available yet. Will be added later.

    • SafeArea props are no longer available. I'll probably add them again later. In the meantime, you can get the same functionality doing the following refactor:

        // v1
        <Box safeAreaTop />
      
        // v2
        import { useSafeAreaInsets } from "react-native-safe-area-context";
        const { top } = useSafeAreaInsets();
      
        <Box top={top}>
      

Upgrading icons

  • SpiroKit v1 was using react-native-heroicons (292 icons)

  • SpiroKit v2 is now using @tamagui/lucide-icons (1237 icons)

  • Components like Button, Alert, Badge, HorizontalCard, VerticalCard, PortraitCard, Input, Tab, TextArea should still work with the old icons, but I highly recommend you to make the switch. You can search for all the available components here

Migration is straightforward:

// v1
import { MapIcon } from "react-native-heroicons/outline"
<Button IconLeftComponent={MapIcon}>

// v2
import { Map } from "@tamagui/lucide-icons"
<Button IconLeftComponent={Map}>

Hooks

useColorMode

  • In v1, there was a "useColorMode" hook inherited from NativeBase. In v2, I decided to remove this hook, and unify all the features related to color mode and accent color in the "useTheme" hook. More information about this in an upcoming post introducing new features related to theming capabilities.

  • If you were using this hook to check for the active color mode, or to toggle between dark and light mode, you can now use the "useTheme" hook to achieve the same

// v1
const { colorMode, toggleColorMode } = useColorMode()

// v2
const { colorMode, setColorMode, accentColor, setAccentColor } = useTheme()

Feedback is appreciated ๐Ÿ™

If you are trying to migrate and you find another issue that is not included on this post, please feel free to reach out to me at mauro@spirokit.com or send me a DM on Twitter

ย