Quickly build a mobile responsive Gym Tracker
Tags: react, nextjs, serverless, functions
Published: 2022-11-24 07:31:31

---
title: Quickly build a mobile responsive Gym Tracker
published: true
description: Learn how to build a Gym Tracker to describe daily activities using Next.js and Xata.
tags: #react #Nextjs #serverless #functions
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/miz4bnhonwednxahvs33.png
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-23 21:28 +0000
---

Trackers are devices or technology that monitor users' activities, events, or page views on a platform. In the proper context, it involves collecting information and monitoring how a user interacts or uses an application. 

Over the years, trackers have been adopted by major applications like food ordering apps, e-commerce, wellness apps, e.t.c, to help their users monitor activities, provide insights, and build consistency.

In this post, we will learn how to build a Gym Tracker to describe daily activities using Next.js and Xata.

## GitHub and Deployment link

The project source codes and deployment link are below:

- [GitHub link](https://github.com/Mr-Malomz/gym-tracker)
- [Live Site](https://gym-trackers.netlify.app/)

## Prerequisites 

To follow along with this tutorial, the following requirements apply:

- Basic understanding of JavaScript and React
- A [Xata account](https://xata.io/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) for storing data. [**Signup**](https://app.xata.io/?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog) **is completely free**

## Getting started

We need to create a Next.js starter project by navigating to the desired directory and running the command below in our terminal.

```bash
npx create-next-app gym-tracker && cd gym-tracker
```

The command creates a Next.js project called `gym-tracker` and navigates into the project directory.

**Installing TailwindCSS**
[TailwindCSS](https://tailwindcss.com/) is a utility-first CSS framework packed with classes to help us style our web pages and make them responsive to various screens. To use it in our application, run the command below:

```bash
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```

The command installs TailwindCSS and its dependencies and generates  `tailwind.config.js` and `postcss.config.js` files.

Next, we need to update `tailwind.config.js` file with the snippet below:

```js
module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './pages/**/*.{js,ts,jsx,tsx}',
    './components/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

Finally, we need to add TailwindCSS directives to our application. The directives give our application access to TailwindCSS utility classes. To do this, navigate to the `styles` folder and update the `globals.css` files in it with the snippet below:

```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```


## Set up a Database on Xata

To create a database for storing our gym activities data, we need to log into our Xata’s workspace, click the **Add a Database** button, input `gym` as the database name, and **Create**.

![Add a Database](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667173516275_Screenshot+2022-10-31+at+00.44.55.png)
![input database name](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667521568046_Screenshot+2022-11-04+at+01.25.52.png)


Next, we need to create a table in our database. To do this, click the **Start from scratch** menu, input `tracker` as the table name, and **Add table**.

![click the start from scratch](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667522004432_Screenshot+2022-11-03+at+11.47.48.png)
![input and create](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667522018922_Screenshot+2022-11-03+at+11.48.16.png)


**PS**: *Xata auto-generates an ID column (a unique identifier) for our table.*

With that done, we need to add a `description` of type **String** and `created_on` of type **Date** columns to our table. To do this, click the **Plus icon**, select type accordingly, input column name, and **Create column**.

![plus icon](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667522519099_Screenshot+2022-10-29+at+16.31.54+copy.png)
![select string](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667174472131_Screenshot+2022-10-31+at+01.00.47.png)
![input name](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667174553609_Screenshot+2022-10-31+at+01.02.02.png)


After creating the columns, our updated table should be similar to the screenshot below:

![updated table](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667522679922_Screenshot+2022-11-04+at+01.42.50.png)


**Get Database URL and set up API Key**
By default, Xata provides a unique and secure URL for accessing the database. To get our database URL, click the **Get code snippet** button, copy the URL, and then click the **Set up API key** button to generate API key. We need the API key for authentication when using the database.

![Get code snippet](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667522773988_Screenshot+2022-11-04+at+01.43.09.png)



![URL and Set up API key](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667175618585_Screenshot+2022-10-29+at+16.40.21.png)


**PS**: *The URL we need to copy starts from the* `https://……` *section*

Click the **Add a key** button, input `xata-function` as the name, and **Save**.

![Add a key](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667175790072_Screenshot+2022-10-29+at+16.42.41.png)
![Copy API Key](https://paper-attachments.dropboxusercontent.com/s_A1144B74EC1072093F0D2701A540D1E74AD827D7B0FFCD60C26EE8B5BA462734_1667175790087_Screenshot+2022-10-29+at+16.46.22.png)



**Setup environment variable**
Next, we need to add our database URL and the API as an environment variable. To do this, we need to create a `.env` file in the root directory and add the snippet below:

```bash
NEXT_PUBLIC_XATA_URL=<REPLACE WITH THE COPIED DATABASE URL>
NEXT_PUBLIC_XATA_API_KEY=<REPLACE WITH THE COPIED API KEY>
```


## Create the Gym Tracker in Next.js

**Create** **application  logic**
To get started, we’ll navigate to our project root directory and create a `components` folder; here, create a `services.js` file and add the snippet below:

```js
export const createTrack = async (description) => {
    const body = {
        description,
        created_on: new Date(),
    };

    const resp = await fetch(
        `${process.env.NEXT_PUBLIC_XATA_URL}:main/tables/tracker/data`,
        {
            method: 'POST',
            body: JSON.stringify(body),
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${process.env.NEXT_PUBLIC_XATA_API_KEY}`,
            },
        }
    );

    return resp;
};

export const getTrackers = async () => {
    const body = {
        page: {
            size: 20,
        },
    };

    const resp = await fetch(
        `${process.env.NEXT_PUBLIC_XATA_URL}:main/tables/tracker/query`,
        {
            method: 'POST',
            body: JSON.stringify(body),
            headers: {
                'Content-Type': 'application/json',
                Authorization: `Bearer ${process.env.NEXT_PUBLIC_XATA_API_KEY}`,
            },
        }
    );

    return resp;
};
```

The snippet above does the following:

- Creates a `createTrack` function that takes in `description` as a parameter to describe gym activity for the day. The function also does the following:
    - **Line 2 - 5**: Creates the request body with the required parameters
    - **Line 7 - 17**: Makes `POST` request to Xata by modifying the database URL to include the **branch**, **table name**, and **query** **type**. It also passes the API key as an authorization header.
    - **Line 19**: Returns the response
- Creates a `getTrackers` function to get the list of activities. The function also does the following:
    - **Line 23 - 27**: Creates a request body to paginate the requested data
    - **Line 29 - 39**: Makes `POST` request to Xata by modifying the database URL to include the **branch**, **table name**, and **query** **type**. It also passes the API key as an authorization header.
    - **Line 41**: Returns the response

The Xata database URL used above was constructed by passing in the **branch**, **table name**, and **endpoint type**.

```bash
process.env.NEXT_PUBLIC_XATA_URL:<BRANCH NAME>/tables/<TABLE NAME>/ENDPOINT TYPE
```

We can get the required details from our workspace

![Branch name and Table name](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667555059885_Screenshot+2022-11-04+at+08.58.03.png)


In our case, we filled it using an environment variable. Adequately filled  URLs are below:

**createTrack**

```bash 
  process.env.NEXT_PUBLIC_XATA_URL:main/tables/trackers/data
```

**getTracker**

```bash  
  process.env.NEXT_PUBLIC_XATA_URL:main/tables/trackers/query
```

**Building the User Interface**
With that done, we can start using our application logic to build our application. To get started, first, we need to create a `Nav.js` file in the `components` folder and add the snippet below:

```js
import Link from 'next/link';
import { useRouter } from 'next/router';

export default function Nav() {
    const router = useRouter();
    return (
        <nav className='border-b-gray-300 h-10 border-b flex mb-6'>
            <img
                src='https://res.cloudinary.com/dtgbzmpca/image/upload/v1667430832/Gym-logo.png'
                alt='gym logo'
                className='mr-4'
            />
            <Link
                href={'/'}
                className={`font-medium text-sm ml-4 ${
                    router.pathname == '/'
                        ? 'border-b-[4px] border-teal-600 text-teal-600'
                        : ''
                }  `}
            >
                Home
            </Link>
            <Link
                href={'/history'}
                className={`font-medium text-sm ml-4 ${
                    router.pathname == '/history'
                        ? 'border-b-[4px] border-teal-600 text-teal-600'
                        : ''
                }  `}
            >
                History
            </Link>
        </nav>
    );
}
```

The snippet above creates the application navigation and sets the active tab accordingly. We also leverage the power of Cloudinary to seamlessly host and deliver our application logo.

Secondly, we need to update the `index.js` file inside the `pages` folder as shown below:

```js
import Head from 'next/head';
import { useState } from 'react';
import Nav from '../components/Nav';
import { createTrack } from '../components/services';

export default function Home() {
    const [description, setDescription] = useState('');
    const [isLoading, setIsloading] = useState(false);

    const handleSubmit = (e) => {
        setIsloading(true);
        e.preventDefault();
        createTrack(description)
            .then((resp) => {
                alert('Tracker created successfully!');
                setDescription('');
                setIsloading(false);
            })
            .catch((e) => {
                alert('Error creating tracker!');
                setIsloading(false);
            });
    };
    return (
        <div>
            <Head>
                <title>Gym Tracker</title>
                <meta
                    name='description'
                    content='Generated by create next app'
                />
                <link rel='icon' href='/favicon.ico' />
            </Head>
            <main className='py-4 px-4 lg:py-10 lg:px-10 w-full'>
                <Nav />
                <section className='flex justify-center'>
                    <div className='px-4 py-2 border rounded-lg w-full lg:w-2/4'>
                        <div className='border-b h-8 mb-4'>
                            <h3 className='text-gray-700'>Gym Tracker</h3>
                        </div>
                        <form onSubmit={handleSubmit}>
                            <fieldset>
                                <label className='text-sm text-gray-400 mb-2 block'>
                                    Description
                                </label>
                                <textarea
                                    name=''
                                    id=''
                                    rows='5'
                                    className='border w-full rounded-sm mb-6 p-2'
                                    required
                                    value={description}
                                    onChange={(e) =>
                                        setDescription(e.target.value)
                                    }
                                />
                            </fieldset>
                            <button
                                className='text-sm text-white px-8 py-2 rounded-sm bg-teal-600 hover:bg-teal-700'
                                disabled={isLoading}
                            >
                                Submit
                            </button>
                        </form>
                    </div>
                </section>
            </main>
        </div>
    );
}
```

The snippet above does the following:

- Imports the required dependencies
- **Line****s** **7-8**: Create state properties to manage application state
- **Line****s** **10-23**: Create an `handleSubmit` function that uses the `createTask` helper function to create an activity
- Adds the UI to use the state and function to create a gym track

Lastly, we need to create a `history.js` file in the same `pages` folder to show the history of the gym activities.

```js
import Head from 'next/head';
import Nav from '../components/Nav';
import { getTrackers } from '../components/services';

export async function getServerSideProps() {
    const response = await getTrackers().then((resp) => resp);
    const data = await response.json();

    return { props: { data } };
}

export default function History({ data }) {
    return (
        <div>
            <Head>
                <title>Gym Tracker</title>
                <meta
                    name='description'
                    content='Generated by create next app'
                />
                <link rel='icon' href='/favicon.ico' />
            </Head>
            <main className='py-4 px-4 lg:py-10 lg:px-10 w-full'>
                <Nav />
                <section className='flex justify-center'>
                    <div className='px-4 py-2 border rounded-lg w-full lg:w-2/4'>
                        <div className='border-b h-8 mb-4'>
                            <h3 className='text-gray-700'>History</h3>
                        </div>
                        <ul>
                            {data.records.length > 0 ? (
                                data.records.map((record) => (
                                    <li
                                        key={record.id}
                                        className='h-auto border rounded-sm px-4 py-2 mb-2'
                                    >
                                        <p className='text-sm text-gray-500 mb-2'>
                                            {record.description}
                                        </p>
                                        <p className='text-xs text-gray-500 mb-2'>
                                            {record.created_on.slice(0, 10)}
                                        </p>
                                    </li>
                                ))
                            ) : (
                                <p className='text-sm text-gray-500 mb-2 text-center'>
                                    You currently do not have any data in your
                                    tracker!.
                                </p>
                            )}
                        </ul>
                    </div>
                </section>
            </main>
        </div>
    );
}
```

The snippet above does the following:

- Imports the required dependencies
- Gets data from Xata using the `getServerSideProps` and the `getTrackers` helper functions
- Updates the UI to show data dynamically from the returned `data` property

With that done, we can restart our development server using the command below:

```bash
npm run dev
```


![Create](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667558702813_Screenshot+2022-11-04+at+11.44.29.png)
![History](https://paper-attachments.dropboxusercontent.com/s_FCD9D830F6428F1CD109C3FE7FBEC3248DA8EAC260696449A52E25A8B5E7846C_1667558702789_Screenshot+2022-11-04+at+11.44.44.png)

## Conclusion

This post discussed how to quickly build a responsive Gym Tracker to describe daily activities using Xata and Cloudinary.

These resources might be helpful:

- [Xata official docs](https://xata.io/docs/overview?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [Cloudinary official docs](https://cloudinary.com/documentation?utm_source=hackmamba&utm_campaign=hackmamba-hackathon&utm_medium=hackmamba-blog)
- [TailwindCSS](https://tailwindcss.com/)