# Setting up a full-stack application with Node, Express, Vite, React, tailwindcss, twin.macro and styled-components

> ### The Stack 🚀
> 
> * **vite**: a fast, lightweight frontend build tool that uses ES modules and service workers to provide a smooth development experience.
>     
> * **tailwindcss**: a utility-first CSS framework for rapidly building custom user interfaces.
>     
> * **twin.macro**: a Babel macro that enables users to write CSS in JavaScript using a Tagged Template Literal syntax similar to styled-components.
>     
> * **styled-components:** a library for creating and styling reusable React components using a familiar CSS-in-JS syntax.
>     
> * **node.js:** an open-source, cross-platform JavaScript runtime environment that executes JavaScript code outside of a web browser.
>     
> * **express**: a minimalist web framework for Node.js that provides a set of features for building web applications and APIs.
>     

### 🤔 Why `vite` over `create-react-app`?

1. Vite is built around the concept of a modern JavaScript bundler called `esbuild`, which is designed to be fast and lightweight. This can result in faster builds and rebuilds when compared to `create-react-app`, which uses `webpack`.
    
2. Vite includes a built-in development server that utilizes native ES module imports and service workers to provide a smooth development experience with hot module reloading (HMR).
    
3. Vite has a simple configuration file `(vite.config.js)` that is easy to understand and customize.
    
4. Vite is designed to be flexible and extensible, and it provides a plugin system that allows users to customize the build process or add additional features.
    
5. Vite has a smaller footprint than `create-react-app`, as it does not include a lot of the boilerplate and configuration that comes with `create-react-app` by default. This can be a benefit for developers who want a simpler, more lightweight tool.
    

### 🤔 Why `twin.macro`, `tailwindcss`, and `styled-components` for styling?

1. `twin.macro` allows you to use the syntax and features of `styled-components` to define your styles, while still taking advantage of the utility-first approach of `tailwindcss`.
    
2. The combination of `twin.macro` and `tailwindcss` can make it easier to build custom user interfaces quickly, as you can use the predefined classes provided by `tailwindcss` to style your components, and use `twin.macro` to override or extend those styles as needed.
    
3. Using `styled-components` with `twin.macro` and `tailwindcss` can help to keep your component-level styles organized and maintainable, as you can define all of the styles for a component in a single place.
    
4. `twin.macro` can help to improve the performance of your application by automatically purging unused styles from your CSS, reducing the size of your stylesheets and improving the loading time of your application.
    
5. The combination of `twin.macro`, `tailwindcss`, and `styled-components` can provide a powerful and flexible toolset for styling your React applications, enabling you to build custom user interfaces with ease.
    

### 🤔 Why `node.js` and `express`?

1. `Node.js` is built on top of the V8 `JavaScript` runtime, which makes it efficient and fast for building scalable network applications.
    
2. `Node.js` has a large and active developer community, which means there are many resources available for learning and troubleshooting.
    
3. `Express` is a minimalist web framework for `Node.js` that provides a set of features for building web applications and APIs. It is easy to learn and use, and it has a small footprint, making it well-suited for intermediate-level full-stack applications.
    
4. `Express` is built on top of the `middleware` concept, which makes it easy to extend and customize the functionality of your application.
    
5. `Node.js` and `Express` are well-suited for building real-time applications, such as *chat applications or collaborative tools*, as they can handle a high number of concurrent connections and provide low latency.
    
6. Both `Node.js` and `Express` have a large ecosystem of third-party libraries and plugins that can be easily integrated into your application, allowing you to build a wide range of functionality with minimal effort.
    

Now that we have set the stack and reasons for using the specifics out of the way let's dive right into the setup process of our application.

> I will be using:
> 
> * **Visual Studio Code (vscode)** for this demonstration as it is my favourite of all other code editors and I am very comfortable using it. You can any code editor of your choice.
>     
> * ***Node Package Manager (NPM)*** as my default package manager, You can also use `yarn` .
>     

### ⚙️ The Setup

#### Step 1️⃣ : Create a project, Initialise/setup the server using `npm` and segregate the backend (server) and frontend (client) responsibilities

* create a new folder and open it inside vscode
    
* open the terminal in vscode using `command + j` on mac and `ctrl + j` on windows
    
* run `npm init -y` in the terminal
    

> `npm init -y` is a command that is used to create a new `package.json` file for a Node.js project. The `-y` flag tells npm to use the default values for the package.json fields, so you don't have to answer any prompts.

* you will a new file `package.json` created in the root of your project looking something like this
    
    ```json
    {
      "name": "fullstackdemo",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC"
    }
    ```
    
* once the `package.json` has been initialised, we need to create a new `.js` file in the root that will contain our main server logic. I will be naming it `index.js`
    
* next, create a new folder in the root of your project *(next to the above created* `package.json`*)* I am naming it `client.` This will contain all our code related to frontend that we will be creating using `vite,` shortly.
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673333440448/2e99abe5-3253-4771-9136-ac2a05dad955.png align="center")
    
* now, we need to install the necessary dependencies on our server-side. in your terminal, run:
    
    ```bash
     npm install concurrently express nodemon
    ```
    
    This will create a `node_modules` folder where all the dependencies for the server will be saved. We can also notice that the `package.json` contents have now been updated.
    
    while we are at our server's `package.json` let's also update the scripts that will run our server and client `concurrently`. We are using `nodemon` to automatically rerun our server whenever we make any changes to our code.
    
    ```json
      "scripts": {
        "server": "nodemon index.js",
        "client": "npm run dev --prefix client",
        "dev": "concurrently \"npm run server\" \"npm run client\""
      },
    ```
    
    and this is how the `package.json` should be looking at this point:
    
    ```json
    {
      "name": "fullstackdemo",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "server": "nodemon index.js",
        "client": "npm run dev --prefix client",
        "dev": "concurrently \"npm run server\" \"npm run client\""
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "concurrently": "^7.6.0",
        "express": "^4.18.2",
        "nodemon": "^2.0.20"
      }
    }
    ```
    

#### Step 2️⃣ : Initialize the React application inside `client` folder using `vite` and configure `vite.config.js` to proxy any API requests to the server

* Inside your `client` folder run:
    
    ```json
    npm create vite@latest
    ```
    
    and complete the prompt by giving the inputs asked. Enter `.` for your project name so that the project is initialised in the `client` folder itself. Choose `react` when asked to select a framework and `javascript` as the variant for this demo.
    
    The folder structure should be looking like this on successful initialisation.
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673335159906/167054bb-018e-4602-844c-5ba3dc70cb39.png align="center")

* next, install all the dependencies that are needed for setting up our react client.
    
    ```bash
    npm install @emotion/core @emotion/react @emotion/styled styled-components twin.macro vite-plugin-babel-macros
    ```
    
* once we have these dependencies installed, we can proceed to set up `tailwindcss` in our react application
    
    > Follow the official tailwind guide to setup with vite here: [https://tailwindcss.com/docs/guides/vite](https://tailwindcss.com/docs/guides/vite)
    > 
    > **Note**: since we have already setup our client folder, we can follow the documentation step 2 onwards.
    
* after completing the `tailwindcss` setup you will have two new files in your client folder, `tailwind.config.cjs` and `postcss.config.cjs`
    
* next, we need to configure vite. This will be done by adding rules to `vite.config.js`
    
    ```javascript
    import { defineConfig } from 'vite';
    import react from '@vitejs/plugin-react';
    import macrosPlugin from 'vite-plugin-babel-macros';
    
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [macrosPlugin(), react()],
      server: {
        port: 3000,
        proxy: {
          '/api': {
            target: 'http://localhost:6001',
            changeOrigin: true,
            secure: false,
            ws: true,
          },
        },
      },
    });
    ```
    
    > **❗️plugins:**
    > 
    > `vite-plugin-babel-macros` is a Vite plugin that allows you to use advanced code transformations (macros) at compile time, in React projects, without a complex build system. It uses `babel-plugin-macros` and allows you to import macros from libraries like `babel-plugin-react-css-modules` to transform CSS modules into CSS classes at compile time, streamlining the development process for React projects.
    > 
    > **❗️server:**
    > 
    > * The `port` property is set to `3000`, which means that the development server will run on port 3000. This means that when you start the development server, you'll be able to access your application in your web browser at [`http://localhost:3000`](http://localhost:3000).
    >     
    > * The `proxy` property is set to an object that defines a single proxy route. The proxy is used to forward certain requests from the development server to another server or service.
    >     
    >     * The route `'/api'` is defined, this is a path that when the development server receives a request for it, will forward the request to the target specified.
    >         
    >     * `target` property is set to [`http://localhost:6001`](http://localhost:6001), This is the URL of the target server. All requests to `/api` will be forwarded to this URL.
    >         
    >     * `changeOrigin: true` change the request's origin from [localhost](http://localhost) to the target origin.
    >         
    >     * `secure: false` this means that self-signed SSL certificates will be accepted.
    >         
    >     * `ws: true` WebSocket proxy, If you're proxying WebSocket connections, this property should be set to `true`.
    >         
    > 
    > This setup allows you to run the frontend and the backend on different ports and different servers but the frontend can make the requests to the backend as if they were on the same origin.
    > 
    > This means that requests to [`http://localhost:3000/api/*`](http://localhost:3000/api/*) will be forwarded to [`http://localhost:6001/*`](http://localhost:6001/*) allowing your front-end to interact with your back-end services as if they were running on the same server.
    > 
    > It's worth noting that the `secure: false` and `ws: true` are optional, You can leave it out or set it to `false` if you don't want it.
    

#### **Step 3️⃣ : Write server code and start the development environment**

> We will be creating a simple API endpoint to fullfill a simple GET request that returns some static data sufficient for this demonstration.

* inside `index.js` of your root directory, add the following code:
    
    ```javascript
    const express = require('express');
    const app = express();
    
    const ENV = 'development';
    const DOMAIN = ENV === 'development' ? 'localhost' : '';
    const PORT = 6001;
    
    app.use(express.json({ extended: false }));
    
    app.get('/api', (req, res) => {
      try {
        const mockData = {
          firstName: 'Adeesh',
          lastName: 'Sharma'
        };
    
        res.json(mockData);
      } catch (err) {
        res.status(500).json({error: err.message });
      }
    });
    
    app.listen(PORT, `${DOMAIN}`, () => {
      console.log(`Server listening on port ${PORT}`);
    });
    ```
    
* now, fire up the dev server by running the following command in the root folder
    
    ```javascript
    npm run dev
    ```
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673338111236/e98bb4b5-b9da-44d8-acd3-829db6572e79.png align="center")
    
* test the `react` application by navigating to `localhost:3000` in the browser
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673338297235/3b3503e9-a769-4497-bb0e-9068b897a25d.png align="center")

* test the API endpoint by hitting `localhost:3000/api`
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673345896439/2817144d-fbed-41a5-9ed6-cd3bdb42bb59.png align="left")

**🎉 We have successfully set up a full-stack application running express in the backend and serving a react client 🎉**

#### Step 4️⃣: Create a simple Application page in react using `tailwindcss` and refactor with `twin.macro`

* contents of `client/src/App.js`
    
    ```javascript
    import React, { useState, useEffect } from 'react';
    import './index.css';
    
    const App = () => {
      const [data, setData] = useState(null);
    
      useEffect(() => {
        const apiCall = async () => {
          const response = await fetch('/api');
          const data = await response.json();
          setData(data);
        };
    
        apiCall();
      }, []);
    
      return (
        <div className='bg-indigo-200 h-screen'>
          <div className='max-w-7xl mx-auto py-16 px-4 sm:py-24 sm:px-6 lg:px-8'>
            <div className='text-center'>
              <h2 className='text-base font-semibold text-indigo-600 tracking-wide uppercase'>
                Demonstration
              </h2>
              <p className='text-red-600 mt-1 text-4xl font-extrabold sm:text-5xl sm:tracking-tight lg:text-6xl'>
                Full-stack application
              </p>
              {data && (
                <p className='max-w-xl mt-5 mx-auto text-xl text-gray-500'>
                  with ♥️ by {data.firstName} {data.lastName}
                </p>
              )}
            </div>
          </div>
        </div>
      );
    };
    
    export default App;
    ```
    
* refactor with `twin.macro` and `styled-components`
    
    ```javascript
    import React, { useState, useEffect } from 'react';
    import './index.css';
    import styled from 'styled-components';
    import tw from 'twin.macro';
    import vite from '/vite.svg';
    
    const App = () => {
      const [data, setData] = useState(null);
    
      useEffect(() => {
        const apiCall = async () => {
          const response = await fetch('/api');
          const data = await response.json();
          setData(data);
        };
    
        apiCall();
      }, []);
    
      return (
        <ApplicationContainer>
          <SectionContainer>
            <Image src={vite} />
            <CenteredText>
              <H2>Demonstration</H2>
              <Title>Full-stack application</Title>
              {data && (
                <Author>
                  with ♥️ by {data.firstName} {data.lastName}
                </Author>
              )}
            </CenteredText>
          </SectionContainer>
        </ApplicationContainer>
      );
    };
    
    export default App;
    
    const ApplicationContainer = tw.div`bg-indigo-200 h-screen`;
    const SectionContainer = tw.div`max-w-7xl mx-auto py-16 px-4 sm:py-24 sm:px-6 lg:px-8`;
    
    const CenteredText = styled.div`
      ${tw`text-center`}
    `;
    
    const H2 = styled.h2`
      font-weight: 800;
      ${tw`text-base text-indigo-600 tracking-wide uppercase`}
    `;
    
    const Title = tw.p`text-red-600 mt-1 text-4xl font-extrabold sm:text-5xl sm:tracking-tight lg:text-6xl`;
    const Author = tw.p`max-w-xl mt-5 mx-auto text-xl text-gray-500`;
    const Image = styled.img`
      ${tw`h-40 flex justify-center w-full`}
    `;
    ```
    
* **Final Product 👏🏻**
    

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1673341584553/c96a0a7e-7ba0-4f21-b761-a86bfa29f7e8.png align="center")

### 🔗 GitHub repository

Link: [https://github.com/adeeshsharma/fullstack-setup](https://github.com/adeeshsharma/fullstack-setup)

### 💭 Conclusion

A development setup that uses Node, Express, Vite, React, Tailwind CSS, twin.macro, and styled-components is an excellent choice for building modern web applications. Each of these technologies offers powerful and flexible features that can help to reduce development time and streamline the process of building web applications.

Node and Express provide a robust and scalable foundation for building back-end services, while Vite offers a fast and easy-to-use development server that allows you to quickly test and iterate on your front-end code. React, on the other hand, is a popular and widely used JavaScript library that makes it easy to build reusable UI components, while Tailwind CSS is a utility-first CSS framework that can help to speed up the process of styling your application.

twin.macro and styled-components can further reduce development time by allowing you to use advanced code transformations at compile time, without having to use a complex build system. twin.macro which is a library that allows you to use macros in your React code and styled-components is a library that allows you to write CSS styles in JavaScript, and it can be used to style React components.

In conclusion, this setup is a powerful and efficient way to build web applications, thanks to the simplicity and flexibility provided by Node, Express, Vite, React, Tailwind CSS, twin.macro and styled-components. It can save you a lot of development time, compared to other frameworks and libraries, by simplifying the process of building modern web applications.
