{"id":20319506,"url":"https://github.com/yakkomajuri/react-from-scratch","last_synced_at":"2025-10-25T21:33:42.473Z","repository":{"id":46121278,"uuid":"340886729","full_name":"yakkomajuri/react-from-scratch","owner":"yakkomajuri","description":"Building a Modern React App from Scratch in 2021","archived":false,"fork":false,"pushed_at":"2022-03-10T10:11:32.000Z","size":1004,"stargazers_count":151,"open_issues_count":2,"forks_count":16,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-11T18:13:07.470Z","etag":null,"topics":["babel","javascript","kea","react","reactjs","redux","typescript","webpack"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yakkomajuri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-02-21T11:40:06.000Z","updated_at":"2025-03-26T22:50:23.000Z","dependencies_parsed_at":"2022-09-24T17:33:45.516Z","dependency_job_id":null,"html_url":"https://github.com/yakkomajuri/react-from-scratch","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/yakkomajuri/react-from-scratch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yakkomajuri%2Freact-from-scratch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yakkomajuri%2Freact-from-scratch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yakkomajuri%2Freact-from-scratch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yakkomajuri%2Freact-from-scratch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yakkomajuri","download_url":"https://codeload.github.com/yakkomajuri/react-from-scratch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yakkomajuri%2Freact-from-scratch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264229436,"owners_count":23576252,"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":["babel","javascript","kea","react","reactjs","redux","typescript","webpack"],"created_at":"2024-11-14T18:47:10.337Z","updated_at":"2025-10-25T21:33:42.355Z","avatar_url":"https://github.com/yakkomajuri.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align='center' style='margin: auto; text-align: center'\u003e\n    \u003cimg src='readme-assets/react.svg' width=25 style='display: inline-block' /\u003e Building a Modern React App from Scratch\n    \u003cimg src='readme-assets/react.svg' width=25 style='display: inline-block' /\u003e\n\u003c/h1\u003e\n\n\u003ch4 align='center'\u003eA step-by-step tutorial to setting up a modern React app with no boilerplate.\u003c/h4\u003e\n\n\u003cbr /\u003e\n\n\u003chr /\u003e\n\n## 📋 Table of Contents\n\n1. [Running as a boilerplate](#%EF%B8%8F-running-as-a-boilerplate)\n1. [Motivation](#-motivation)\n1. [Objective](#-objective)\n1. [Tooling](#%EF%B8%8F-tooling)\n1. [Getting Started](#%EF%B8%8F-getting-started)\n1. [Setup](#%EF%B8%8F-setup)\n    - [Babel](#babel)\n    - [TypeScript](#typescript)\n    - [Webpack](#webpack)\n    - [React](#react)\n    - [Kea](#kea)\n    - [package.json](#packagejson)\n1. [Finally, some React code](#-finally-some-react-code)\n    - [But first, some vanilla HTML](#but-first-some-vanilla-html)\n    - [The entry point](#the-entry-point)\n    - [Our App!](#our-app)\n    - [Writing JS and TS side-by-side](#writing-js-and-ts-side-by-side)\n    - [Counter](#counter)\n\n\u003chr /\u003e\n\n## ⏭️ Running as a boilerplate\n\nIf you want to use this repo as a boilerplate, you can simply do:\n\n```shell\ngit clone https://github.com/yakkomajuri/react-from-scratch\ncd react-from-scratch\nyarn\nyarn start\n```\n\nThe app will be available on `localhost:3000`.\n\n## 💪 Motivation \n\nThe main objective of this tutorial for me was to get myself to better understand the multiple moving parts that make a React app work, rather than just accepting the \"magic\" of the many templates/boilerplates out there, like [create-react-app](https://create-react-app.dev/), and [react-boilerplate](https://www.reactboilerplate.com/).\n\nIt was very much inspired by the extremely well-written \u003ci\u003e[Creating a React App… From Scratch.](https://blog.usejournal.com/creating-a-react-app-from-scratch-f3c693b84658)\u003c/i\u003e by [@paradoxinversion](https://github.com/paradoxinversion), an article that is so good it is referenced in the [official React Docs](https://reactjs.org/docs/create-a-new-react-app.html#creating-a-toolchain-from-scratch).\n\nHowever, times change, and I wanted to build a modern React app from scratch following the latest developments. As such, I had a few more \"essentials\" to include in the toolchain, and wanted to work with the latest versions of core libraries. In some ways, I see this as the `latest` version of the tutorial mentioned above.\n\n## 🎯 Objective\n\nMy goal here is simple: build a React app from \"scratch\". From scratch here doesn't mean building the supporting tools myself, but rather taking responsibility for their setup, rather than outsourcing it to something like `create-react-app`.\n\nHowever, beyond setting up a React app that just works, I also had a few more requirements, pertaining to what many would deem \"essentials\" of the modern stack:\n\n1. It must support TypeScript\n2. It should have state management provisioned out of the gate\n\n## ⚒️ Tooling\n\nSo what exactly do I need to make this work?\n\nTo find the answer, I started with the React Docs. \n\nReading \u003ci\u003e[Creating a Toolchain from Scratch](https://reactjs.org/docs/create-a-new-react-app.html#creating-a-toolchain-from-scratch\n)\u003c/i\u003e tells me the following about what I need:\n\n\u003cblockquote\u003e\n\n* A **package manager**, such as Yarn or npm. It lets you take advantage of a vast ecosystem of third-party packages, and easily install or update them.\n* A **bundler**, such as webpack or Parcel. It lets you write modular code and bundle it together into small packages to optimize load time.\n* A **compiler** such as Babel. It lets you write modern JavaScript code that still works in older browsers.\n\n\u003c/blockquote\u003e\n\nThis short snippet tells me quite a bit about what I need and why I need it. So I made my picks:\n\n* **Package manager:** [Yarn](https://yarnpkg.com/)\n* **Bundler:** [Webpack](https://webpack.js.org/)\n* **Compiler:** [Babel](https://babeljs.io/)\n\nThese are pretty standard choices. Even if you haven't set these up yourself before, you've probably dealt with them, or at least heard about them at some point. \n\nHowever, based on my requirements, I still have one thing missing - a state management library.\n\n[Redux](https://redux.js.org/) would have been the straightforward choice, but I went with [Kea](https://kea.js.org/). Kea is in fact built _on top of_ Redux, so I'll effectively be using Redux under the hood, but it makes state management significantly easier. \n\nFor full disclosure, I am definitely biased - the reason for choosing Kea is simply that I use it [at work](https://github.com/PostHog/posthog), and its [author](https://github.com/mariusandra) is my co-worker. \n\n## ▶️ Getting Started\n\nThe first thing we need is a new directory. Set that up and then run `yarn init` inside of it to get started.\n\nWhen it asks you for the \"entry point\", use `src/index.tsx`. You'll know why in a second. \n\nInside your directory, create 2 more: `src` and `public`. \n\n`src` will host the entire source code for our project, while `public` will be where we put the static assets. \n\n## ⚙️ Setup\n\nRather than being a one-size-fits-all tutorial, this is meant to be a learning process, and dealing with issues that arise is inevitably an important part of it. \n\nHence, I won't be tagging version numbers on installations. You can check the versions being used in `package.json` if you want to use this as a boilerplate.\n\nAs an example, I decided to use Webpack v5 for this tutorial, which brought me some compatibility issues with configs I was originally using from Webpack v4 projects. As always, with enough docs, articles, and StackOverflow posts, I got through it, and learned more in the process.\n\n### Babel \n\nGetting Babel to work requires quite a few packages, you can install them like this:\n\n```shell\nyarn add --dev \\\n  @babel/core \\\n  @babel/cli \\\n  @babel/preset-env \\\n  @babel/preset-typescript \\\n  @babel/preset-react\n```\n\n`babel-core` is the compiler, the main thing we need.\n\n`babel-cli` will let us use the compiler via the CLI.\n\nThe last three packages are Babel \"templates\" (presets), for dealing with various use cases. `preset-env` is used to prevent us from having headaches, allowing us to write modern JS while ensuring the output will work across clients. `preset-typescript` and `preset-react` are quite self-explanatory: we're using both TypeScript and React, so we'll be needing them.\n\nFinally, we need to set up a `babel.config.js` file, specifying to the compiler the presets we're using:\n\n```js\n// babel.config.js\nmodule.exports = {\n    presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],\n}\n```\n\n### TypeScript\n\nWe want to use TypeScript in our project, so that has its own setup beyond the Babel preset.\n\nFirst, we need the `typescript` package:\n\n```shell\nyarn add --dev typescript\n```\n\nThen, being proactive, I suggest you also get the following packages if you'll be following this tutorial until the end:\n\n```shell\nyarn add --dev @types/react @types/react-dom @types/react-redux\n```\n\nThese packages contain the type declaration for the modules we'll be using throughout the project.\n\nAnd we also need a `tsconfig.json` file - I'm using [the config from here, which we use in production](https://github.com/PostHog/posthog.com/blob/master/tsconfig.json):\n\n```js\n{\n    \"compilerOptions\": {\n        \"baseUrl\": \"./src\",\n        \"paths\": {\n            \"types/*\": [\"./types/*\"]\n        },\n        // https://www.sitepoint.com/react-with-typescript-best-practices/\n        \"allowJs\": true, // Allow JavaScript files to be compiled\n        \"skipLibCheck\": true, // Skip type checking of all declaration files\n        \"esModuleInterop\": true, // Disables namespace imports (import * as fs from \"fs\") and enables CJS/AMD/UMD style imports (import fs from \"fs\")\n        \"allowSyntheticDefaultImports\": true, // Allow default imports from modules with no default export\n        \"strict\": true, // Enable all strict type checking options\n        \"forceConsistentCasingInFileNames\": true, // Disallow inconsistently-cased references to the same file.\n        \"module\": \"esnext\", // Specify module code generation\n        \"moduleResolution\": \"node\", // Resolve modules using Node.js style\n        \"resolveJsonModule\": true, // Include modules imported with .json extension\n        \"noEmit\": true, // Do not emit output (meaning do not compile code, only perform type checking)\n        \"jsx\": \"react\", // Support JSX in .tsx files\n        \"sourceMap\": true, // Generate corrresponding .map file\n        \"declaration\": true, // Generate corresponding .d.ts file\n        \"noUnusedLocals\": true, // Report errors on unused locals\n        \"noUnusedParameters\": true, // Report errors on unused parameters\n        \"experimentalDecorators\": true, // Enables experimental support for ES decorators\n        \"noFallthroughCasesInSwitch\": true, // Report errors for fallthrough cases in switch statement\n        \"lib\": [\"dom\", \"es2019.array\"]\n    },\n    \"include\": [\"src/**/*\"],\n    \"exclude\": [\"node_modules/**/*\", \"staticfiles/**/*\", \"frontend/dist/**/*\"]\n}\n```\n\nFeel free to change some of the configuration above to better suit your needs. However, it's important that you keep these options:\n\n```\n\"noEmit\": true, // Do not emit output (meaning do not compile code, only perform type checking)\n\"jsx\": \"react\", // Support JSX in .tsx files\n```\n\n`\"jsx\": \"react\"` is self-explanatory. As for `noEmit`, the reason we should have this as true is because Babel is compiling the TypeScript for us, so we just want `typescript` to be used to check for errors (e.g. while we're writing code).\n\n\u003e Sidenote: [Comments are allowed in `tsconfig.json` files](https://github.com/Microsoft/TypeScript/pull/5450)\n\n### Webpack\n\nWebpack also needs a lot of stuff to work. Essentially, for every type of file we want to bundle, we'll need a specific loader.\n\nHence, here's what we need:\n\n```shell\nyarn add --dev \\\n    webpack \\\n    webpack-cli \\\n    webpack-dev-server \\\n    style-loader \\\n    css-loader \\\n    babel-loader\n```\n\n`webpack` and `webpack-cli` follow the same principle as Babel - one is the core package and the other let's us access those tools from the CLI.\n\n`webpack-dev-server` is what we need for local development. You'll notice that `package.json` never actually references it from a script, but it is required to run `webpack serve`:\n\n```\n[webpack-cli] For using 'serve' command you need to install: 'webpack-dev-server' package\n```\n\nFinally, the loaders are what we need for the different files we want to process. A `ts-loader` also exists, but, since we're using Babel to compile our JS files, we don't actually need it.\n\nAnd, like with Babel, we need a `webpack.config.js` file:\n\n```js\n// webpack.config.js\nconst path = require('path')\nconst webpack = require('webpack')\n\nmodule.exports = {\n    entry: './src/index.tsx', // our entry point, as mentioned earlier\n    mode: 'development',\n    module: {\n        rules: [\n            {\n                test: /\\.[jt]sx?$/, // matches .js, .ts, and .tsx files\n                loader: 'babel-loader', // uses babel-loader for the specified file types (no ts-loader needed)\n                exclude: /node_modules/, \n            },\n            {\n                test: /\\.css$/, // matches .css files only (i.e. not .scss, etc)\n                use: ['style-loader', 'css-loader'], \n            },\n        ],\n    },\n    resolve: {\n        extensions: ['.tsx', '.ts', '.js'],\n    },\n    output: {\n        filename: 'bundle.js', // our output bundle\n    },\n    devServer: {\n        contentBase: path.join(__dirname, 'public/'),\n        port: 3000,\n        publicPath: 'http://localhost:3000/dist/',\n        hotOnly: true,\n    },\n    plugins: [new webpack.HotModuleReplacementPlugin()], // used for hot reloading when developing\n    devtool: 'eval-source-map', // builds high quality source maps\n}\n\n```\n\n### React\n\nGiven that this is a React app, we need some React packages too!\n\nThis should be enough:\n\n```shell\nyarn add react react-dom react-hot-loader\n```\n\n`react` is self-explanatory. `react-dom` will be used to render our app on `index.tsx`, and `react-hot-loader` is used for development - it will auto update our app on file changes.\n\n### Kea\n\nLastly, we need to set up our state management library, Kea.\n\nFrom the [Kea Docs](https://kea.js.org/docs/installation/instructions), here's what you need:\n\n```shell\nyarn add kea redux react-redux reselect\n```\n\nWe'll think ahead here as well and also grab us a separate package used when Kea logic is written in TypeScript:\n\n```shell\nyarn add --dev kea-typegen\n```\n\n### package.json\n\nWith all this set up, we should add a few useful scripts to our `package.json` file:\n\n```json\n    ...\n    \"scripts\": {\n        \"start\": \"webpack serve --mode development\",\n        \"typegen\": \"kea-typegen write ./src\"\n    },\n    ...\n```\n\n`start` will be used to run our server, and `typegen` to generate types for our Kea logic files.\n\n## 💻 Finally, some React code\n\nQuite a bit of setup, huh? I guess we should be thankful for boilerplates, especially when they manage all the dependencies and versioning for us (_cough_ [react-scripts](https://github.com/facebook/create-react-app/tree/master/packages/react-scripts)).\n\nNevertheless, we're now done with setup, so onto some code!\n\n### But first, some vanilla HTML\n\nThe first thing we need is an `index.html` file, that React will use to render our app. It is the only `.html` file we'll have. This is also the only file we'll have in `public/` in this tutorial.\n\nHere's my `index.html`:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n    \u003chead\u003e\n        \u003cmeta charset=\"UTF-8\" /\u003e\n        \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" /\u003e\n        \u003ctitle\u003eReact from Scratch\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n        \u003cdiv id=\"root\"\u003e\u003c/div\u003e\n        \u003cnoscript\u003e You need to enable JavaScript to access this website. \u003c/noscript\u003e\n        \u003cscript src=\"../dist/bundle.js\"\u003e\u003c/script\u003e\n    \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThere are a few things happening here:\n* We're setting a few default meta tags, as well as a title for our website\n* We specified a `root` div, which we'll use to render our App (this is essentially the starting point from which the inner HTML will be dynamically-generated by React)\n* We added a message for those that have JavaScript disabled, as our app won't work for them\n* We imported our finished Webpack bundle, which we haven't actually generated yet\n    * This will contain all the code we write in a single file\n\n### The entry point\n\nRemember the mention to entry point from earlier? Well now we've gotten to it. Go into the `src/` subdir and make a new file called `index.tsx`. \n\nHere's what I have in mine:\n\n```js\nimport React from 'react'\nimport ReactDOM from 'react-dom' \nimport { Provider } from 'react-redux'\nimport { getContext, resetContext } from 'kea'\nimport { App } from './App'\n\nresetContext({\n    createStore: {},\n    plugins: [],\n})\n\nReactDOM.render(\n    \u003cProvider store={getContext().store}\u003e\n        \u003cApp /\u003e\n    \u003c/Provider\u003e,\n    document.getElementById('root')\n)\n```\n\nThere are 3 key things happening here:\n1. We're setting up Kea, which, like Redux, uses `Provider` to make the store available to any nested components (in this case, our entire app)\n    - The `resetContext` call is not actually needed here, since we're not passing anything to it. However, I've left it here so you know where to add, for example, your Kea plugins, since you'll likely use those\n2. We're importing and rendering our `App` component (which we haven't built yet)\n3. We're telling React to render our app using our `root` div from `index.html` as the \"binding point\"\n\n### Our App!\n\nNow, create a file called `App.tsx`, also inside `src/`, with the following:\n\n```js\nimport React from 'react'\nimport { hot } from 'react-hot-loader/root'\nimport { MyJSComponent } from './components/MyJSComponent'\nimport { Counter } from './components/Counter'\n\nexport const App = hot(_App)\nexport function _App(): JSX.Element | null {\n    return (\n        \u003cdiv\u003e\n            \u003ch1\u003eHello world!\u003c/h1\u003e\n            \u003cMyJSComponent /\u003e\n            \u003cCounter /\u003e\n        \u003c/div\u003e\n    )\n}\n```\n\nIf you just want to see your app working at this point, you can remove the imports and references to `MyJSComponent` and `Counter` and run `yarn start`. This will start your server and you should be able to access your React app at `localhost:3000`, receiving your 'Hello world!' greeting from it.\n\nThe reason I've included these two extra components is to test that we have a few things working:\n1. We can write JavaScript alongside TypeScript\n2. Our state management is working fine\n3. Our bundler processes `.css` files with no problem (`Counter` has some minimal styling)\n\nHence, you could stop here if you wanted to. But if you want to see these 3 things in action, read on.\n\n ### Writing JS and TS side-by-side\n\nAs you saw in our `App.tsx` file, we have a TypeScript file importing a JavaScript file with no problems. \n\nThe reason this works is because of this rule we have in `webpack.config.js`:\n\n```js\n{\n    test: /\\.[jt]sx?$/,\n    loader: 'babel-loader',\n    exclude: /node_modules/,\n},\n```\n\nRemove the `j` from `test` and we wouldn't be able to use JS files with TS files.\n\nTo test that everything is working fine, I simply created a tiny JS component and imported it into app.\n\nI created it in a new directory called `components/`, and here's what it contains:\n\n```js\nimport React from 'react'\n\nexport const MyJSComponent = () =\u003e \u003ch2\u003eTry out the counter below!\u003c/h2\u003e\n```\n\n### Counter\n\nThe last thing I added to this project, while still keeping it minimal, is the traditional React counter component. \n\nThe goal here is to test that our Kea setup works, as well as that importing CSS files works too.\n\nSo, I first created a subdir inside `components/` called `Counter`. Here I added 3 files:\n\n#### index.tsx\n\nIncludes the actual component. Here it is:\n\n\n```js\nimport React, { useState } from 'react'\nimport { useValues, useActions } from 'kea'\nimport { counterLogic } from './counterLogic'\nimport './style.css'\n\nexport const Counter = () =\u003e {\n    const { count } = useValues(counterLogic)\n    const { incrementCounter, decrementCounter, updateCounter } = useActions(counterLogic)\n\n    const [inputValue, setInputValue] = useState(0)\n\n    return (\n        \u003cdiv\u003e\n            \u003ch3\u003e{count}\u003c/h3\u003e\n            \u003cdiv\u003e\n                \u003cbutton onClick={incrementCounter}\u003e+\u003c/button\u003e\n                \u003cbutton onClick={decrementCounter}\u003e-\u003c/button\u003e\n            \u003c/div\u003e\n            \u003cbr /\u003e\n            \u003cdiv\u003e\n                \u003cinput type=\"number\" value={inputValue} onChange={(e) =\u003e setInputValue(Number(e.target.value))} /\u003e\n                \u003cbutton onClick={() =\u003e updateCounter(inputValue)}\u003eUpdate Value\u003c/button\u003e\n            \u003c/div\u003e\n        \u003c/div\u003e\n    )\n}\n```\n\nPretty simple stuff. Click `+` and the count goes up, `-` and the count goes down. Set any number using the input and count will be updated too.\n\nAlso notice the `style.css` import.\n\n#### counterLogic.ts\n\n`counterLogic.ts` hosts the logic for manipulating the state that our `Counter` component uses. I won't explain how Kea works here, but the following is pretty self-explanatory:\n\n```js\nimport { kea } from 'kea'\nimport { counterLogicType } from './counterLogicType'\n\nexport const counterLogic = kea\u003ccounterLogicType\u003e({\n    actions: {\n        incrementCounter: true, // https://kea.js.org/docs/guide/concepts#actions\n        decrementCounter: true, // true is shorthand for a function that doesn't take any arguments\n        updateCounter: (newValue: number) =\u003e ({ newValue }),\n    },\n    reducers: {\n        count: [\n            0, // default value\n            {\n                incrementCounter: (state) =\u003e state + 1,\n                decrementCounter: (state) =\u003e state - 1,\n                updateCounter: (_, { newValue }) =\u003e newValue, // ignore the state, set new value\n            },\n        ],\n    },\n})\n```\n\n#### style.css\n\nHere I just have the most minimal styling I could think of, just to test that CSS is working as intended:\n\n```css\nh3 {\n    color: blue;\n}\n```\n\n#### What about counterLogicType.ts?\n\nGood question. If you explore the code in this repo you will see a `counterLogicType.ts` file inside the `Counter` directory.\n\nThis file is automatically generated by `kea-typegen` and contains the types for the `counterLogic`. It was generated by running `yarn typegen`, leveraging the command we added to `package.json` earlier. Usually, one shouldn't commit these files, since they're only useful in development, but I've left this one here so you can see what it looks like. \n\n## That's it!\n\nIf you've gotten all the way down here, hopefully you've come out of it with a shiny new React app, a modern boilerplate, and some additional knowledge. Honestly, this is just me documenting a bit of my learning process, but hopefully you got something out of it too!\n\nIf you have any feedback or suggestions, feel free to open an issue for it. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyakkomajuri%2Freact-from-scratch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyakkomajuri%2Freact-from-scratch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyakkomajuri%2Freact-from-scratch/lists"}