DEV Community

Cover image for Starting React Native Project in 2023
Vladimir Vovk
Vladimir Vovk

Posted on • Updated on

 

Starting React Native Project in 2023

Updated to Expo 47.

There are many ways to start a new React Native project. Here we will be using Expo's create-expo-app command line tool because:

  • It has nice defaults out of the box.
  • It is compatible with Expo packages, which are usually very good, well-tested, and have excellent documentation.
  • We can easily switch to a "bare" react-native app at any moment with one npx expo eject command. See the docs to learn about the difference between managed and bare workflows.

Also, we will add TypeScript, ESLint, Prettier, and some custom configurations that will make our development process better.

TLDR You can use one command npx create-expo-app -t expo-ts to create new React Native project with all tools already setup for you (see README for details) or follow instructions below. πŸ€“

Please refer to the official React Native and Expo documentation for more details. 🀩

General setup

We will need Node.js, Git and Yarn before we start.

Please check React Native Setup Guide to ensure that everything is installed on your machine.

Awesome! πŸ‘πŸ»
Now let's create a new app.

  1. Run npx create-expo-app command.
  2. Type your project name.
  3. Change directory to your project with cd <your-project-name> command.
  4. Run yarn start to start Metro Bundler.
  5. Press i to start the iOS simulator or a to run the Android emulator.πŸ“±

Tools

TypeScript

Let's add TypeScript support.

  1. Create an empty tsconfig.json file in your project root: touch tsconfig.json.
  2. Rename App.js to App.tsx: mv App.js App.tsx.
  3. Run yarn start. It will prompt you to install the required dependencies (typescript, @types/react, @types/react-native), and automatically configure your tsconfig.json.

Absolute path imports

To use absolute path imports, e.g. import { ComponentA } from 'src/components/A' (notice path starts with src), we need to add baseUrl and paths parameters to the compilerOptions of tsconfig.json.

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "src/*": ["src/*"]
    },
    "strictNullChecks": true
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Also we need to create src/package.json file.

{
  "name": "src"
}
Enter fullscreen mode Exit fullscreen mode

Also, we will need to install the babel-plugin-module-resolver to be able to run our project on web.

☝🏻 You can safely skip it, if you targeting only mobile platforms.

yarn add --dev babel-plugin-module-resolver
Enter fullscreen mode Exit fullscreen mode

Specify the plugin configuration in babel.config.js:

module.exports = function(api) {
  api.cache(true);
  return {
    // ... presets settings goes here
    plugins: [
      [
        'module-resolver',
        {
          root: ['.'],
          alias: {
            src: './src'
          }
        }
      ],
    ]
  };
};
Enter fullscreen mode Exit fullscreen mode

Move App.tsx to src folder

It's good to have all source files in one folder. So let's move App.tsx to src with mv App.tsx src command.

Next, we need to create a new App.js file inside our project's folder with touch App.js. And import our app logic there:

import App from 'src/App'

export default () => <App />
Enter fullscreen mode Exit fullscreen mode

Prettier

Prettier is an opinionated code formatter. Let's install it.

yarn add -D prettier
Enter fullscreen mode Exit fullscreen mode

We will also need .prettierrc.js config file in the project root.

module.exports = {
  semi: false,
  trailingComma: 'none',
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
}
Enter fullscreen mode Exit fullscreen mode

Sort imports

Unsorted imports look ugly. Also, it could be hard to read and add new imports. So why not sort them automatically? We can do it with trivago/prettier-plugin-sort-imports.

yarn add --dev @trivago/prettier-plugin-sort-imports
Enter fullscreen mode Exit fullscreen mode

Add plugin configuration to the Prettier config .prettierrc.js:

module.exports = {
  // ... prettier config here

  importOrderSeparation: true,
  importOrderSortSpecifiers: true,
  importOrderCaseInsensitive: true,
  importOrder: [
    '<THIRD_PARTY_MODULES>',
    // '^(.*)/components/(.*)$', // Add any folders you want to be separate
    '^(.*)/(?!generated)(.*)/(.*)$', // Everything not generated
    '^(.*)/generated/(.*)$', // Everything generated
    '^[./]' // Absolute path imports
  ]
}
Enter fullscreen mode Exit fullscreen mode

Check code for errors

We can use TypeScript compiler and ESLint for this.

TypeScript Compiler

Let's add new check-typescript script to our package.json.

...
"scripts": {
  ...
  "check-typescript": "tsc --noEmit"
},
...
Enter fullscreen mode Exit fullscreen mode

Now we can run yarn check-typescript command to check our code for errors with TypeScript compiler.

ESLint

ESLint has a lot configuration options and rules. Let's start with Expo eslint-config-universe package.

yarn add --dev eslint-config-universe
yarn add --dev eslint prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser
yarn add --dev eslint-plugin-react-hooks
yarn add --dev eslint-import-resolver-typescript
Enter fullscreen mode Exit fullscreen mode

Add .eslintrc.js config file to the project root.

module.exports = {
  extends: ['universe', 'universe/shared/typescript-analysis', 'plugin:react-hooks/recommended'],
  overrides: [
    {
      files: ['*.ts', '*.tsx', '*.d.ts'],
      parserOptions: {
        project: './tsconfig.json'
      }
    }
  ],
  settings: {
    'import/resolver': {
      typescript: {} // this loads <rootdir>/tsconfig.json to ESLint
    }
  },
  /* for lint-staged */
  globals: {
    __dirname: true
  }
}

Enter fullscreen mode Exit fullscreen mode

Add new check-eslint script to our package.json.

...
"scripts": {
  ...
  "check-eslint": "eslint './src/**/*{js,ts,jsx,tsx}'"
},
...
Enter fullscreen mode Exit fullscreen mode

Now we can run yarn check-eslint command to check our code for errors with ESLint. And yarn check-eslint --fix to fix errors automatically.

Lint script

Let's combine TypeScript and ESLint checks together so we can run both at once.

Add new lint script to our package.json.

...
"scripts": {
  ...
  "lint": "yarn check-typescript && yarn check-eslint"
},
...
Enter fullscreen mode Exit fullscreen mode

Changelog

We can use standard-version tool to generate a changelog, bump the version of app and create a new tag automatically.

How It Works:

yarn add --dev standard-version
Enter fullscreen mode Exit fullscreen mode

Create the .versionrc.js config:

module.exports = {
  types: [
    { type: 'feat', section: 'New features' },
    { type: 'fix', section: 'Bug fixes' },
    { type: 'change', section: 'Changes' },
    { type: 'chore', hidden: true },
    { type: 'docs', hidden: true },
    { type: 'style', hidden: true },
    { type: 'perf', hidden: true },
    { type: 'test', hidden: true }
  ]
}
Enter fullscreen mode Exit fullscreen mode

In this config, we enable the feat, fix, and change commit types. And if you want to enable the other commit types, you can remove the hidden boolean and replace it with the section string and provide a title.

Add new release script to package.json:

...
"scripts": {
  ...
  "release": "standard-version"
},
...
Enter fullscreen mode Exit fullscreen mode

Now when you ready to release, just run yarn release command.

Husky

Husky improves your commits and more 🐢 woof!

We will use Husky to check if our commit messages follow the conventional commits rules, run the lint check, format staged code with Prettier and ESLint.

yarn add --dev husky
yarn add --dev @commitlint/config-conventional @commitlint/cli
yarn add --dev lint-staged
Enter fullscreen mode Exit fullscreen mode

Create a config for commitlint with commitlint.config.js file:

module.exports = {
  extends: ['@commitlint/config-conventional']
}
Enter fullscreen mode Exit fullscreen mode

Setup lint-staged with package.json > lint-staged configuration:

...
  "lint-staged": {
    "**/*.{js,jsx,ts,tsx}": [
      "eslint './src/**/*{js,ts,jsx,tsx}' --fix",
      "prettier --write './src/**/*{js,ts,jsx,tsx}'"
    ]
  },
...
Enter fullscreen mode Exit fullscreen mode

Configure Husky, add package.json > prepare script and run it once:

npm set-script prepare "husky install"
npm run prepare
Enter fullscreen mode Exit fullscreen mode

husky install will ensure that every developer using this repo will install Husky Hooks before using the project.

Add pre-commit hook:

npx husky add .husky/pre-commit 'npx --no-install lint-staged'
Enter fullscreen mode Exit fullscreen mode

Add commit message hook:

npx husky add .husky/commit-msg 'yarn lint && npx --no-install commitlint --edit "$1"'
Enter fullscreen mode Exit fullscreen mode

SafeAreaContext

react-native-safe-area-context provides a flexible API for accessing device safe area inset information.

npx expo install react-native-safe-area-context
Enter fullscreen mode Exit fullscreen mode

Wrap your App component with SafeAreaProvider:

import { SafeAreaProvider } from 'react-native-safe-area-context'

function App() {
  return <SafeAreaProvider>...</SafeAreaProvider>
}
Enter fullscreen mode Exit fullscreen mode

And now we can use SafeAreaView component.

SafeAreaView is a regular View component with the safe area insets applied as padding or margin.

What to add next?

Credits

Photo by SpaceX on Unsplash.

You can find the πŸ‘΄πŸ» 2022 version here.


Please post your favorite tools in the comments, press πŸ’– button, and happy hacking! πŸ™ŒπŸ»

Top comments (0)

typescript

11 Tips That Make You a Better Typescript Programmer

1 Think in {Set}

Type is an everyday concept to programmers, but it’s surprisingly difficult to define it succinctly. I find it helpful to use Set as a conceptual model instead.

#2 Understand declared type and narrowed type

One extremely powerful typescript feature is automatic type narrowing based on control flow. This means a variable has two types associated with it at any specific point of code location: a declaration type and a narrowed type.

#3 Use discriminated union instead of optional fields

...

Read the whole post now!