Setting Up for React Native Development on MacOS
Installation and Setup
Pre-requisites
React Native setup documentation.
- Install Xcode
- Install Xcode Command Line Tools
- Install Homebrew
- Install Mise
- Install Node via Mise
- Install Watchman via Homebrew
- Install a Java Development Kit (JDK), Android Studio, the Android SDK: follow the multistep directions
Setting up the first time
Install the Expo CLI Globally:
# sh
npm install -g @expo/cli
Create a mise.toml
file:
# sh
mise use node@lts
Create the app:
# sh
npx create-expo-app MyApp && cd MyApp
Reset the project to move the starter code to an app demo directory (per the Readme file):
# sh
npm run reset-project
Edit the app/(tabs)/index.ts
file.
Make a components
directory at the same level as app
to store component files.
Ensure code consistency with Prettier and fix formatting issues with ESLint
Configure prettier, add as a dev dependency:
# sh
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser @eslint/eslintrc
Create a .prettierrc
file in the project root. This follows the conventions most common in Expo projects such as a 100 character line length, use of single instead of double quotes, a tab width of 2, keeping >
on the same line as the last prop, adding spaces inside {}
, and the use of ES5 comma conventions (such as in array and object definitions but not function parameters or arguments):
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"bracketSameLine": true,
"arrowParens": "avoid"
}
Create a .prettierignore
file to exclude certain files:
node_modules
.expo
.expo-shared
*.log
.DS_Store
*.tgz
*.tar.gz
.cache
dist
build
Update the eslint.config.js
file, adding to the defineConfig
array arg:
{
files: ['**/*.{js,jsx,ts,tsx}'],
plugins: {
prettier: require('eslint-plugin-prettier'),
},
rules: {
// Prettier integration
'prettier/prettier': 'error',
// Unused imports - TypeScript projects
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true
}],
// For JavaScript files
'no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true
}],
},
},
// This should come last to disable conflicting rules
require('eslint-config-prettier'),
Update the scripts section of the package.json
to include:
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check ."
Edit the mise.toml
file to pin Node and npm and add tasks (run mise tasks
to see a list of tasks or mise run start
to run the Expo Go start task for example):
[tools]
# Use the current LTS version of Node.js (after running `mise use node@lts`, run `node --version` to find the specific version)
node = "22.17.0"
# Pin npm version (run `npm --version` to find the specific version)
npm = "10.9.2"
[env]
# Expo CLI environment variables
EXPO_USE_DEV_SERVER = "true"
# Prevent auto-opening browser on expo start
EXPO_NO_BROWSER = "true"
[tasks.start]
run = "npx expo start"
description = "Start Expo development server"
[tasks.start-clean]
run = "npx expo start --clear"
description = "Clear cache and start Expo development server"
[tasks.build-ios]
run = "npx expo build:ios"
description = "Build for iOS"
[tasks.ios]
run = "npx expo run:ios"
description = "Run on iOS simulator"
[tasks.iphone]
# May have to set up Xcode the first time to install Developer Certificates and do a build of the app on the device
run = "npx expo run:ios --device"
description = "Run on iPhone via USB connection"
[tasks.android]
run = "npx expo run:android"
description = "Run on Android emulator"
[tasks.test]
run = "npm test"
description = "Run tests"
[tasks.lint-fix]
run = "npm run lint:fix"
description = "Fix code quality issues and format with prettier"
[tasks.prettier]
run = "npm run format"
description = "Format code with prettier"
Add a Pre-Commit Hook to Check Code with Prettier
Install Husky and Lint-Staged packages to perform this task
# sh
npm install --save-dev husky lint-staged
Initialise Husky, this will update .husky/pre-commit
and tell it to run npx lint-staged
when committing:
# sh
npx husky init
To bypass the check, add the --no-verify
, or shorthand -n
flag to the Git commit e.g. git commit -n -m "my message"
. To disable hooks completely run git config core.hooksPath ""
and to re-enable them run git config --unset core.hooksPath
. Husky can be disabled specifically via an environment variable: export HUSKY=0 git commit -m "my message"
.
Configure PHPStorm/PyCharm
Open Settings, navigate Language & Frameworks -> JavaScript -> Code Quality Tools -> ESLint. Automatic ESLint configuration should be selected by default. Tick the option to "Run eslint --fix on save."
To use Tailwind CSS
NativeWind is a utility-first styling library that brings Tailwind CSS to React Native. Installation instructions .
Note the current directions refer to editing the tailwind.config.js
file with reference to an App.tsx
file as the main content point, however, using the setup directions above, the file will actually be ./app/_layout.tsx
where the CSS is imported and the correct content attribute in tailwind.config.js
should be: content: ["./app/*.{js,jsx,ts,tsx}", "./components/**/*.{js,jsx,ts,tsx}"],
.
Optionally Update the Prettier Configuration
Warning: this does not play well with the PHPStorm/PyCharm autoformatting we set above, so best to skip it in that case. Otherwise, update the .prettierrc
file to include prettier-plugin-tailwindcss
as a plugin:
{
...
"plugins": ["prettier-plugin-tailwindcss"]
}
Update the ESLint Configuration
Update the eslint.config.js
file to reference the plugin as well:
...
module.exports = defineConfig([
...
{
...
plugins: {
prettier: require('eslint-plugin-prettier'),
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'),
},
...
]);
Update the ESLint Configuration when using PHPStorm/PyCharm to auto format on save:
Update the eslint.config.js
file to include the tailwind plugin:
const tailwind = require('eslint-plugin-tailwindcss'); // <-- new
...
module.exports = defineConfig([
...
{
...
plugins: {
prettier: require('eslint-plugin-prettier'),
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'), // <-- from the step above
tailwindcss: tailwind, // <-- new
},
rules: {
... // <-- prettier integration
// Disable Tailwind classnames-order since PyCharm handles it
'tailwindcss/classnames-order': 'off', // <-- new
'tailwindcss/no-custom-classname': 'off', // <-- new
... // <-- other rules
}
...
]);
Feedback?
Email us at enquiries@kinsa.cc.