{"id":20466006,"url":"https://github.com/atolye15/crna-recipe","last_synced_at":"2025-04-13T08:54:32.442Z","repository":{"id":48938655,"uuid":"180127008","full_name":"atolye15/crna-recipe","owner":"atolye15","description":"Step-by-step guide to bootstrap a React Native app from scratch","archived":false,"fork":false,"pushed_at":"2020-10-19T11:01:09.000Z","size":717,"stargazers_count":189,"open_issues_count":4,"forks_count":13,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-03-27T00:33:55.723Z","etag":null,"topics":["react","react-native","storybook","typescript"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/atolye15.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-08T10:37:50.000Z","updated_at":"2024-09-08T16:38:28.000Z","dependencies_parsed_at":"2022-09-11T21:34:56.632Z","dependency_job_id":null,"html_url":"https://github.com/atolye15/crna-recipe","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atolye15%2Fcrna-recipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atolye15%2Fcrna-recipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atolye15%2Fcrna-recipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atolye15%2Fcrna-recipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atolye15","download_url":"https://codeload.github.com/atolye15/crna-recipe/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248688543,"owners_count":21145763,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-native","storybook","typescript"],"created_at":"2024-11-15T13:21:06.506Z","updated_at":"2025-04-13T08:54:32.416Z","avatar_url":"https://github.com/atolye15.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Native App Creation Recipe\n\nThis is a step-by-step guide to create React Native app for Atolye15 projects. You can review [React Native App Starter](https://github.com/atolye15/crna-starter) project to see how your application looks like when all steps followed.\n\nYou will get an application which has;\n\n* TypeScript\n* Linting\n* Formatting\n* Testing\n* CI/CD\n* Storybook\n\n## Table of Contents\n\n* [Step 1: Installing the React Native CLI](#step-1-installing-the-react-native-cli)\n* [Step 2: Creating a new app](#step-2-creating-a-new-app)\n* [Step 3: Make TypeScript more strict](#step-3-make-typescript-more-strict)\n* [Step 4: Installing Prettier](#step-4-installing-prettier)\n* [Step 5: Installing ESLint](#step-5-installing-eslint)\n* [Step 6: Setting up our test environment](#step-6-setting-up-our-test-environment)\n* [Step 7: Setting up config variables](#step-7-setting-up-config-variables)\n* [Step 8: Organizing Folder Structure](#step-8-organizing-folder-structure)\n* [Step 9: Adding Storybook](#step-9-adding-storybook)\n* [Step 10: Adding CircleCI config](#step-10-adding-circleci-config)\n* [Step 11: Github Settings](#step-11-github-settings)\n* [Step 12 Final Touches](#step-12-final-touches)\n* [Step 13: Starting to Development \u003cg-emoji class=\"g-emoji\" alias=\"tada\" fallback-src=\"https://github.githubassets.com/images/icons/emoji/unicode/1f389.png\"\u003e🎉\u003c/g-emoji\u003e](#step-13-starting-to-development-)\n* [Bonus: Npm Script Aliases](#bonus-npm-script-aliases)\n\n## Step 1: Installing the React Native CLI\n\nFirst of all, we need to install the React Native command line interface.\n\n```bash\nyarn global add react-native-cli\n```\n\n## Step 2: Creating a new app\n\nUse the React Native command line interface to generate a new React Native project called \"AwesomeProject\":\n\n```bash\nreact-native init AwesomeProject --template typescript\n```\n\n\u003e _NOTE: Project name should be alphanumeric!_\n\n## Step 3: Make TypeScript more strict\n\nWe want to keep type safety as strict as possibble. In order to do that, we update `tsconfig.json` with the settings below. Also we prefer to disable `isolatedModules` and activate `skipLibCheck`.\n\n```json\n\"strict\": true,\n\"noImplicitAny\": true,\n\"noImplicitReturns\": true,\n\"skipLibCheck\": true,\n\"isolatedModules\": false,\n```\n\n## Step 4: Installing Prettier\n\nWe want to format our code automatically. So, we need to install Prettier.\n\n```bash\nyarn add prettier --dev\n```\n\n```json\n// .prettierrc\n\n{\n  \"printWidth\": 100,\n  \"singleQuote\": true,\n  \"trailingComma\": \"all\"\n}\n```\n\nAlso, we want to enable format on save on VSCode.\n\n\u003e _React Native CLI adds `.vscode` to `.gitignore`, but we prefer not to ignore. So remove it from `.gitignore`._\n\n```json\n// .vscode/settings.json\n\n{\n  \"editor.formatOnSave\": true\n}\n```\n\nFinally, we update `package.json` with related format scripts.\n\n```json\n\"format\": \"prettier --write 'src/**/*.{ts,tsx}'\",\n\"format:check\": \"prettier -c 'src/**/*.{ts,tsx}'\"\n```\n\n## Step 5: Installing ESLint\n\nWe want to have consistency in our codebase and also want to catch mistakes. So, we need to install ESLint.\n\n```bash\nyarn add eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-eslint-comments eslint-plugin-import eslint-plugin-jest eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-native @typescript-eslint/eslint-plugin @typescript-eslint/parser --dev\n```\n\n```json\n// .eslintrc\n\n{\n  \"parser\": \"@typescript-eslint/parser\",\n  \"extends\": [\n    \"airbnb\",\n    \"plugin:@typescript-eslint/recommended\",\n    \"plugin:prettier/recommended\",\n    \"plugin:eslint-comments/recommended\",\n    \"plugin:import/errors\",\n    \"plugin:import/warnings\",\n    \"plugin:import/typescript\",\n    \"plugin:jest/recommended\"\n  ],\n  \"env\": {\n    \"browser\": true,\n    \"jest\": true,\n    \"react-native/react-native\": true\n  },\n  \"plugins\": [\n    \"react\",\n    \"react-native\",\n    \"@typescript-eslint\",\n    \"jsx-a11y\",\n    \"import\",\n    \"prettier\",\n    \"jest\",\n    \"eslint-comments\"\n  ],\n  \"rules\": {\n    \"@typescript-eslint/indent\": \"off\",\n    \"@typescript-eslint/explicit-function-return-type\": \"off\",\n    \"@typescript-eslint/no-use-before-define\": \"off\",\n    \"react/jsx-filename-extension\": [1, { \"extensions\": [\".js\", \".jsx\", \".ts\", \".tsx\"] }],\n    \"react/prop-types\": \"off\",\n    \"react/button-has-type\": \"off\",\n    \"no-use-before-define\": \"off\",\n    \"import/no-extraneous-dependencies\": [\n      \"error\",\n      {\n        \"devDependencies\": [\n          \"storybook/**/*.{ts,tsx,js}\",\n          \"config-overrides.js\",\n          \"src/setupTests.ts\",\n          \"src/components/**/*.stories.tsx\",\n          \"src/styles/**/*.stories.tsx\",\n          \"src/**/*.test.{ts,tsx}\"\n        ]\n      }\n    ],\n    \"react-native/no-unused-styles\": \"error\",\n    \"react-native/no-inline-styles\": \"error\",\n    \"react-native/no-color-literals\": \"error\",\n    \"react/jsx-one-expression-per-line\": \"off\",\n    \"@typescript-eslint/explicit-member-accessibility\": \"off\",\n    \"prettier/prettier\": [\"error\"]\n  },\n  \"overrides\": [\n    {\n      \"files\": [\"*.style.ts\"],\n      \"rules\": {\n        \"@typescript-eslint/camelcase\": \"off\"\n      }\n    },\n    {\n      \"files\": [\"*.stories.tsx\", \"*.test.tsx\"],\n      \"rules\": {\n        \"@typescript-eslint/no-explicit-any\": \"off\",\n        \"react-native/no-color-literals\": \"off\",\n        \"react-native/no-inline-styles\": \"off\"\n      }\n    }\n  ]\n}\n```\n\nalso ignore some files/folders;\n\n```text\n# .eslintignore\n\nios\nandroid\nbuild\ncoverage\n\n# Storybook\nstorybook/storyLoader.js\n```\n\nWe need to update `package.json` for ESLint scripts.\n\n```json\n\"lint:eslint\": \"eslint 'src/**/*.{ts,tsx}'\",\n\"lint:ts\": \"tsc \u0026\u0026 yarn lint:eslint\",\n\"lint\": \"yarn lint:ts\",\n\"format\": \"prettier --write 'src/**/*.{ts,tsx}' \u0026\u0026 yarn lint:eslint --fix\",\n```\n\nFinally, we need to enable prettier ESLint integration on VSCode.\n\n```json\n// .vscode/settings.json\n\n{\n  // ... ,\n  \"eslint.validate\": [\n    \"javascript\",\n    \"javascriptreact\",\n    { \"language\": \"typescript\", \"autoFix\": true },\n    { \"language\": \"typescriptreact\", \"autoFix\": true }\n  ]\n}\n```\n\n## Step 6: Setting up our test environment\n\nWe'll use `jest` with `react-native-testing-library`.\n\n```bash\nyarn add react-native-testing-library --dev\n```\n\nAdd the following script into `package.json`\n\n```json\n\"test\": \"jest\",\n\"test:watch\": \"yarn test --watch\",\n\"coverage\": \"yarn run test --coverage\"\n```\n\nand then update `jest.config.js` as follows to complete jest configuration.\n\n```js\n{\n  // ... ,\n  collectCoverageFrom: [\n    'src/**/*.{ts,tsx}',\n    '!src/index.tsx',\n    '!src/setupTests.ts',\n    '!src/components/**/index.{ts,tsx}',\n    '!src/**/*.stories.{ts,tsx}',\n    '!src/**/*.style.ts',\n    '!src/styles/**/*',\n  ],\n  coverageThreshold: {\n    global: {\n      branches: 80,\n      functions: 80,\n      lines: 80,\n      statements: 80,\n    },\n  },\n}\n```\n\nLet's add a simple test to verify our setup.\n\n```tsx\n// src/App.test.tsx\n\nimport 'react-native';\nimport React from 'react';\nimport { shallow } from 'react-native-testing-library';\n\nimport App from './App';\n\nit('renders correctly', () =\u003e {\n  const comp = shallow(\u003cApp /\u003e);\n\n  expect(comp.output).toMatchSnapshot();\n});\n```\n\nAlso, verify coverage report with `yarn coverage`.\n\nWhen you run `yarn coverage`, a folder named `coverage` will be created in the root directory. This folder is auto-generated file. We should add it to `.gitignore`\n\n```text\n# .gitignore\n\n...\n# Test Coverage\ncoverage\n```\n\n## Step 7: Setting up config variables\n\nWe use the [react-native-config](https://github.com/luggit/react-native-config) package to expose config variables to our javascript code in React Native.\n\nFollow [these steps](https://github.com/luggit/react-native-config#setup) to install.\n\n## Step 8: Organizing Folder Structure\n\nOur folder structure should look like this;\n\n```text\nsrc/\n├── App.test.tsx\n├── App.tsx\n├── __snapshots__\n│   └── App.test.tsx.snap\n├── components\n│   └── Button\n│       ├── Button.style.ts\n│       ├── Button.stories.tsx\n│       ├── Button.test.tsx\n│       ├── Button.tsx\n│       ├── __snapshots__\n│       │   └── Button.test.tsx.snap\n│       └── index.ts\n├── containers\n│   └── Like\n│       ├── Like.tsx\n│       └── index.ts\n├── index.tsx\n├── screens\n│   ├── Feed\n│   │   ├── Feed.style.ts\n│   │   ├── Feed.test.tsx\n│   │   ├── Feed.tsx\n│   │   ├── index.ts\n│   │   └── tabs\n│   │       ├── Discover\n│   │       │   ├── Discover.style.ts\n│   │       │   ├── Discover.test.tsx\n│   │       │   ├── Discover.tsx\n│   │       │   └── index.ts\n│   │       └── MostLiked\n│   │           ├── MostLiked.test.tsx\n│   │           ├── MostLiked.tsx\n│   │           └── index.ts\n│   ├── Home\n│   │   ├── Home.style.ts\n│   │   ├── Home.test.tsx\n│   │   ├── Home.tsx\n│   │   └── index.ts\n│   └── index.ts\n├── styles\n│   ├── Colors.ts\n│   ├── Spacing.ts\n│   ├── Typography.ts\n│   └── index.ts\n└── utils\n    ├── location.test.ts\n    └── location.ts\n```\n\n## Step 9: Adding Storybook\n\nWe need to initialize the Storybook on our project. We'll use automatic setup with a few edits:\n\n```bash\nnpx -p @storybook/cli sb init --type react_native\n```\n\n\u003e _Warning: Probably after you have run the command above, you'll be asked to select a version. Cancel it._\n\nStorybook CLI automatically installs `v5.0.x`, however `v5.0.x` is an unpublished version for react-native, therefore problems arise during installation. In order to avoid this problem we're going to fix our storybook packages in our `package.json` file to latest stable version `4.1.x`. (Check [this issue](https://github.com/storybooks/storybook/issues/5893) for more information.)\n\n```json\n\"@storybook/addon-actions\": \"^4.1.16\",\n\"@storybook/addon-links\": \"^4.1.16\",\n\"@storybook/addons\": \"^4.1.16\",\n\"@storybook/react-native\": \"^4.1.16\",\n```\n\nthereafter in order to activate the changes and update `yarn.lock` file we'll run code below;\n\n```bash\nyarn\n```\n\nAfter completing steps above you'll notice that storybook CLI have created `storybook` folder on your project's root folder. We'll customize this folder structure according to our use case.\n\nFirstly change the name of `index.js` file in `storybook` folder to `storybook.ts`. Also change file extensions of other files from `js` to `ts`, except the `addons.js` file (https://github.com/storybooks/storybook/issues/3970).\n\nAfter that, we create a new file named `index.ts` to expose StorybookUI in your app.\n\n```js\n// storybook/index.ts\n\nimport StorybookUI from './storybook';\n\nexport default StorybookUI;\n```\n\nWe finished the storybook installation but we are not done yet;\n\nThe stories for our app will be inside the `src/components` directory with the `.stories.tsx` extension.The React Native packager resolves all the imports at build-time, so it's not possible to load modules dynamically. we need to use a third party loader [react-native-storybook-loader](https://github.com/elderfo/react-native-storybook-loader) to automatically generate the import statements for all stories.\n\n```bash\nyarn add react-native-storybook-loader --dev\n```\n\nYou need to update `storybook.ts` as follows:\n\n\u003e _Note: Do not forget to replace `%APP_NAME%` with your app name_\n\n```js\n// storybook/storybook.ts\n\nimport { AppRegistry } from 'react-native';\nimport { getStorybookUI, configure } from '@storybook/react-native';\nimport { loadStories } from './storyLoader';\n\nimport './rn-addons';\n\n// import stories\nconfigure(() =\u003e {\n  loadStories();\n}, module);\n\n// Refer to https://github.com/storybooks/storybook/tree/master/app/react-native#start-command-parameters\n// To find allowed options for getStorybookUI\nconst StorybookUIRoot = getStorybookUI({});\n\n// If you are using React Native vanilla write your app name here.\n// If you use Expo you can safely remove this line.\nAppRegistry.registerComponent('%APP_NAME%', () =\u003e StorybookUIRoot);\n\nexport default StorybookUIRoot;\n```\n\nThe file `storyLoader.js` that we imported above is an auto-generated file. We should add it to `.gitignore`.\n\n```text\n# .gitignore\n\n...\n# Storybook\nstorybook/storyLoader.js\n```\n\nAfter you install storybook loader, you should run the following command once to avoid typescript errors.\n\n```bash\nyarn rnstl\n```\n\nUpdate the storybook script into `package.json` as follows:\n\n```json\n\"storybook\": \"watch rnstl ./src --wait=100 | storybook start | yarn start --projectRoot storybook --watchFolders $PWD\"\n```\n\nAdd the following config into `package.json`:\n\n```json\n// package.json\n{\n  \"config\": {\n    \"react-native-storybook-loader\": {\n      \"searchDir\": [\"./src\"],\n      \"pattern\": \"**/*.stories.tsx\",\n      \"outputFile\": \"./storybook/storyLoader.js\"\n    }\n  }\n}\n```\n\n\u003e _Warning: If you get typescript errors related with the storybook, you should disable `isolatedModules` in `tsconfig.json`_\n\nLastly, because we use typescript in the project, we need to install the type definition for storybook.\n\n```bash\nyarn add @types/storybook__react-native --dev\n```\n\nLet's create an example story for our Button component.\n\n```tsx\n// src/components/Button/Button.stories.tsx\n\nimport React from 'react';\nimport { storiesOf } from '@storybook/react-native';\n\nimport Button from './Button';\n\nstoriesOf('Button', module)\n  .add('Primary', () =\u003e \u003cButton theme=\"primary\"\u003ePrimary Button\u003c/Button\u003e)\n  .add('Secondary', () =\u003e \u003cButton theme=\"secondary\"\u003eSecondary Button\u003c/Button\u003e);\n```\n\n## Step 10: Adding CircleCI config\n\nWe can create a CircleCI pipeline in order to CI / CD.\n\n```yaml\n# .circleci/config.yml\n\nversion: 2\njobs:\n  build_dependencies:\n    docker:\n      - image: circleci/node:10\n    working_directory: ~/repo\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo\n      - restore_cache:\n          keys:\n            - dependencies-{{ checksum \"package.json\" }}\n            - dependencies-\n      - run:\n          name: Install\n          command: yarn install\n      - save_cache:\n          paths:\n            - ~/repo/node_modules\n          key: dependencies-{{ checksum \"package.json\" }}\n      - persist_to_workspace:\n          root: .\n          paths: node_modules\n\n  test_app:\n    docker:\n      - image: circleci/node:10\n    working_directory: ~/repo\n    steps:\n      - checkout\n      - attach_workspace:\n          at: ~/repo\n      - run:\n          name: Generate Storyloader\n          command: yarn rnstl\n      - run:\n          name: Lint\n          command: yarn lint\n      - run:\n          name: Format\n          command: yarn format:check\n      - run:\n          name: Coverage\n          command: yarn coverage\n\nworkflows:\n  version: 2\n  build_app:\n    jobs:\n      - build_dependencies\n      - test_app:\n          requires:\n            - build_dependencies\n```\n\nAfter that we need to enable CircleCI for our repository.\n\n## Step 11: Github Settings\n\nWe want to protect our `develop` and `master` branches. Also, we want to make sure our test passes and at lest one person reviewed the PR. In order to do that, we need to update branch protection rules like this in GitHub;\n\n![github-branch-settings](https://user-images.githubusercontent.com/1801024/55028896-df2a3300-5019-11e9-9827-a984fcfa29af.png)\n\n## Step 12 Final Touches\n\nWe are ready to develop our application. Just a final step, we need to update our `README.md` to explain what we add a script so far.\n\n[EXAMPLE_README](EXAMPLE_README.md)\n\n## Step 13: Starting to Development 🎉\n\nEverything is done! You can start to develop your next awesome React Native application now on 🚀\n\n## Bonus: Npm Script Aliases\n\n### React-Native Alias\n\n```bash\nyarn rn\n```\n\n`rn` alias for `react-native` allows to run react-native CLI command via locally installed react-native.\n\n```json\n// package.json\n\n\"rn\": \"react-native\",\n```\n**NOTE:** Only works with yarn.\n\n### Run On Aliases\n\n```bash\nyarn ios\nyarn run ios\n\nyarn android\nyarn run android\n\n```\n\n`ios` and `android` aliases are helpful when we need to pass different parameter for our project and provides single point entry.\n\n```json\n// package.json\n\n\"ios\": \"yarn rn run-ios\",\n\"android\": \"yarn rn run-android\",\n```\n\n#### Example\n\nIf we want to run our app on iPhone X as default and with scheme just specify that in the alias.\n\n```json\n// package.json\n\n\"ios\": \"yarn rn run-ios --simulator 'iPhone X' --scheme 'Production'\",\n```\n\n### Clear React Native Cache Alias\n\n```bash\nyarn clear-rn-cache\n```\n\n```json\n// package.json\n\n\"clear-rn-cache\": \"watchman watch-del-all \u0026\u0026 rm -rf $TMPDIR/react-* \u0026\u0026 rm -rf $TMPDIR/metro* \u0026\u0026 rm -rf $TMPDIR/haste-*\"\n```\n\n## Related\n\n* [cra-recipe](https://github.com/atolye15/cra-recipe/) - CRA Recipe\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatolye15%2Fcrna-recipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatolye15%2Fcrna-recipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatolye15%2Fcrna-recipe/lists"}