# Welcome to Gravity

Gravity supports **two architectures**:

* **Node.js** → Separate backend + frontend apps
* **Next.js** → Unified app with backend + frontend together

This documentation is split into five sections:

1. [Gravity Server](/gravity-server/introduction) (the back-end Node.js or Next.js application)
2. [Gravity Web](/gravity-web/introduction) (the front-end React client)
3. [Gravity Native](/gravity-native/introduction) (the front-end React Native client)
4. [Mission Control](/mission-control/introduction) (the SaaS admin dashboard)
5. [Website Template](/website-template/introduction) (SaaS website/landing page template)

If you're new to Gravity, start with the [Gravity Server](/gravity-server/installation) installation instructions – this is the backbone of all clients.

Once you've got the server application installed and running, you can work through the [Gravity Web](/gravity-web/introduction) and [Gravity Native](/gravity-native/installation) installation instructions.


# Getting Started

Build a To Do list application in 30 minutes

To help you get up and running as fast as possible, I've put together a 30-minute video tutorial on building an AI photo generation app with Gravity.

The video will show you the overall architecture of the boilerplate and how to scaffold new features quickly.

{% hint style="info" %}
The video uses the Node.js version of Gravity, but the underlying principles are the same for the Next.js version.
{% endhint %}

{% embed url="<https://youtu.be/EDiHBedjYmg?si=c9M-LLNe8BA3RxLQ>" %}

{% hint style="info" %}
The boilerplate code is updated every week, so items may have changed. Please check the description on YouTube for any important changes.
{% endhint %}


# Stack

The Gravity stack is democratically selected by customers. Everyone has a voice in voting for new features and improvements on the roadmap.

### Stack

* React&#x20;
* Vite
* Shadcn
* Radix UI
* React Native *(Gravity Native plan only).*
* Tailwind
* Node.js *or* Next.js (depending on which version you have purchased)
* Express *(Node.js version only)*
* Helmet *(Node.js version only)*
* Knex
* Stripe
* OpenAI
* Bcrypt
* Chart.js
* Nodemailer&#x20;
* Passport.js *(Node.js version only)*
* *Arctic (Next.js version only)*
* MySQL or Postgres or MongoDB
* Chai
* Mocha


# Updates

Gravity is regularly maintained with consistent updates, including new features and bug fixes.

Updates are delivered in the private Github repo.

It's recommended to integrate the Github repo into your workflow so that you can easily merge  updates into your modified codebase.&#x20;

## Versioning&#x20;

Each new commit will have an associated version number in the package.json indicating whether it is a new major release, bug fix or feature update.

| Status            | Version Example |
| ----------------- | --------------- |
| New major release | 10.0.0          |
| Bug fix           | 10.0.1          |
| New feature       | 10.1.0          |


# Rules For AI

This page is your cheat sheet for getting AI tools to generate code that fits the Gravity architecture.

Whether you're building views, setting up routes, styling with Tailwind, or wiring API calls, these rules ensure the AI follows the same patterns used across the codebase so everything stays consistent.

### Supported AI Tools

Gravity includes pre-configured rules for multiple AI environments:

* **Cursor** → `.cursorrules`
* **Claude** → `claude.md`
* **Agents (multi-step workflows)** → `agents.md`
* **GitHub Copilot** → `copilot-instructions.md`
* **Windsurf** → `windsurf.rules`

Each file is tailored to how that tool reads instructions, but they all enforce the same core patterns.

#### Domain-Specific Rules

On top of tool-specific configs, Gravity includes **domain-specific rules** that define:

* Project structure
* Naming conventions
* Component and view patterns
* API and data-fetching patterns

These ensure that regardless of the AI tool you use, the output aligns with how Gravity is built.

#### By Architecture

**Next.js**&#x20;

* Unified rule system for frontend + backend
* Covers routing, server actions, and co-located logic

**Node.js**&#x20;

* Rules are split across:
  * Server (API, services, DB)
  * Web (React client)


# Troubleshooting

Common issues and how to fix them.

## Axios 404 error in the client

The sever is not running or the client can not reach it.

Often the node server will not start because there is already an instance of node running on port 8080. Kill the other instances and try again.

```javascript
killall node
npm run dev // or nodemon in server folder
```

When the server is running you will see the `Welcome to Gravity 🚀` message in your terminal console.

***

## CLI toolbet command not found

The setup script will run `npm link` but sometimes it may fail, run it again manually.

```javascript
npm link
```

***

## Setup script did not complete

This is usually an issue on certain windows shells.

Replace the ; in the `setup` script in `package.json` with &&

```javascript
"setup": "node bin/installcheck && npm install && npm link && node bin/clientcheck && cd ../client && npm install && cd ../server && node bin/appcheck && cd ../app && npm install && cd ../server && node bin/setup.js && npm run dev",
```

***

## Email template not found

Run the seed file to populate the database.

```
// sql
node seeds/sql

// mongo
node seeds/mongo
```

***

## Can't log into Mission Control

`TOKEN_SECRET` in your Mission Control .`env` file does not match the `TOKEN_SECRET` in `/server`.

***

### MSSQL Data Type Mismatch

MSSQL uses NVARCHAR for the foreign key IDs. If you're using MSSQL you will need to update each specificType ID column in the migrations:\
\
Change:\
`table.specificType('id', 'char(36) primary key');`&#x20;

To:

`table.string('id', 36).notNullable().primary();`&#x20;


# Introduction

The Gravity Server powers your application’s core infrastructure — including authentication, billing, APIs, and business logic.

#### By Architecture

**Next.js** \
The server is built directly into your Next.js app.\
There’s no separate backend. API routes, server actions, and logic all live in one codebase.

**Node.js**\
Gravity Server is a standalone backend application.\
\
It connects to:

* Gravity Web (React frontend)
* Gravity Native (React Native app)


# Installation

Before you can take over the world with your new app idea, you'll need to set up a few things first.&#x20;

Don't worry – this is easy and shouldn't take more than 10 minutes (you may even have completed some of these steps already).

{% hint style="info" %}
If you purchased the Power plan. Please also refer to the [Gravity Native set up documentation.](/gravity-native/installation)
{% endhint %}

What we're going to cover in this section:

1. [Install Node.js](/gravity-server/installation/install-node.js)
2. [Create an empty database](/gravity-server/installation/database-setup)
3. [Register a Stripe account for payments](/gravity-server/installation/stripe-setup) (Gravity web only)
4. [Register a Mailgun account (for sending emails)](/gravity-server/installation/mailgun-setup)
5. [Install Gravity](/gravity-server/installation/install-gravity)

Ready? Let's dive in and get started!


# Install Node.js

If you're new to using [Node.js](https://nodejs.org), you will need to install it on your development machine. Head over to [nodejs.org](http://nodejs.org/) and download and install the latest version.

{% hint style="info" %}
If you've already got [Node.js](https://nodejs.org) installed, you can [skip to the next section and set up your database.](/gravity-server/installation/database-setup)
{% endhint %}

### Install Node Package Manager (NPM)

NPM is a huge repository of code modules that you can download and use in your projects. [Gravity](https://usegravity.app) relies on several third-party libraries, so go ahead and [download and install NPM.](https://www.npmjs.com/get-npm)

[<br>](https://www.npmjs.com/get-npm)


# Database Setup

You'll need an empty database for storing your application data. You don't need to create any tables or documents – Gravity will do that automatically for you.

{% hint style="warning" %}
MySQL & Mongo database drivers are included by default. If you're using another database client eg. Postges then please install the relevant driver with npm.
{% endhint %}

Take note of your database credentials, you'll need these during the [setup process](/gravity-server/installation/install-gravity).


# Stripe Setup

Gravity handles the hard work of creating and managing subscriptions in your application, you just need to [create a Stripe account](https://dashboard.stripe.com/register) and configure a few settings before you can start processing payments.

{% hint style="warning" %}
While you can run Stripe in test mode without an SSL certificate, you will need one before you can use Stripe in your live application.
{% endhint %}

## 1. Register Your Stripe Account

Head over to[ Stripe and create your account](https://dashboard.stripe.com/register). Setup is free – you'll only be charged a small fee on each transaction that you process.

## 2. Activate Test Data

Once you've registered and signed in to your account, you'll be presented with your Stripe dashboard.

Stripe has two development modes:&#x20;

1. Test
2. Live&#x20;

Test mode is a great feature that enables you to build and test your payment engine without using  real credit cards. You can use test card numbers to test different scenarios and error messages.

Go ahead and toggle the **Test Mode** switch at in the main left menu to start working in test mode.

{% hint style="info" %}
When using Stripe in test mode, you can use the test credit card details:

**Card Number:** 4242 4242 4242 4242\
**Expiry Date:** 04 32\
**CCV:** 424\
**Postcode:** 42424
{% endhint %}

## 3. Create a Product

Next, click on **Products** and click the + **Add Product** button.

Enter a product name (the name of your application), description and statement descriptor here, which is the name that will appear on your customer's credit card statement (it's only required in live mode).

## 4. Add Pricing Plans

Next, create your pricing plan(s).&#x20;

{% hint style="info" %}
Use something easy to identify, lik&#x65;**:** **plan\_startup** to make your life easier when coding the back-end of your application.
{% endhint %}

Select **Standard pricing,** set the price and currency and set the interval to **recurring** to create a subscription. You can also set the billing period here.

<div align="left"><img src="/files/OhXBf9Wqos0zo77aJBU6" alt="" width="276"></div>

## 5. Copy The API Keys

Finally, make a note of the API keys. \
\
Stripe has two API keys:&#x20;

1. Publishable Key (used client-side)
2. Secret Key (used server-side)

There are two sets of keys for working with live data and test data. \
\
During development, you'll use the test API keys, but when you deploy your application – ensure you switch these to the live keys.

Click on **Developers** > **API Keys**

Copy the API keys somewhere safe for now. You'll need them soon.

That's it for Stripe. There are a lot of other options you can customise. Please refer to the [Stripe Documentation](https://stripe.com/docs) for further information.


# Mailgun Setup

{% hint style="info" %}
Gravity supports any email service supported by [Nodemailer](https://nodemailer.com/). If you don't want to use Mailgun please refer to the [Email Notifications](/gravity-server/email-notifications) section for instructions on how to change to another provider.&#x20;
{% endhint %}

[Mailgun](https://mailgun.com/) is an easy-to-use and very cost-effective transactional email provider with great analytics tools.&#x20;

## 1. Register a Mailgun Account

Head over to [Mailgun](https://signup.mailgun.com/new/signup) and [register your account.](https://signup.mailgun.com/new/signup)

## 2. Setup Your Domain

Follow the [Mailgun setup instructions](https://documentation.mailgun.com/en/latest/quickstart-sending.html) to set up and verify your domain.

You will also need access to your domain control panel to edit the DNS settings. It can take up to 48 hours for your domain to be verified but usually only takes a few minutes.

## 3. Copy Your API Key

Log into your [Mailgun dashboard](https://app.mailgun.com/app/dashboard) and copy your private API key from the sidebar. You'll need this in the next step.


# Install Gravity

Finally, time for the fun part – let's run your new application.

### 1. Clone The Repos

Create a new folder for your project and clone all the repos you were invited to into the project root, eg.

```bash
git clone https://github.com/.../server
git clone https://github.com/.../client-react-web
git clone https://github.com/.../mission-control
git clone https://github.com/.../website
git clone https://github.com/.../client-react-native 

```

You should now have a folder structure with a server subfolder, mission control (Node.js only) and at least one client folder, depending on which plan you purchased.

* server (or next)
* client-react-web
* client-react-native
* mission-control
* website

### 2. Install Packages

Open up a new terminal window and navigate to the folder where you saved Gravity, go into the server folder and run the following command:

```javascript
npm run setup
```

Gravity will also rename the client folders to **client** and **app** and install the packages for the server and client(s).

{% hint style="info" %}
If you experience any issues during this process, you can run a manual install using the following commands.&#x20;

```javascript
npm install
npm link
cd client (Node.js version only)
npm install (Node.js version only)

// optional for mobile app
cd app
npm install
```

{% endhint %}

### 3. Setup Wizard&#x20;

Once the installation has been completed, Gravity will start the server and client and open a new browser window with the homepage, please navigate to <http://localhost:3000/setup>.

You'll be presented with the Gravity welcome screen. Follow the instructions on screen to connect to your database and connect your [Stripe](/gravity-server/installation/stripe-setup) and email accounts.

You can manually configure your application in the [/config](/gravity-server/config) folder if you have any problems during setup.

{% hint style="warning" %}
If your server is running remotely and not localhost, you will need to update the `server_url` in client/src/setting.json&#x20;
{% endhint %}

{% hint style="warning" %}
If you're using MongoDB, you will need to run the seed file manually:&#x20;

**node seeds/mongo**
{% endhint %}

{% hint style="danger" %}
Once you have completed these steps, you **must restart your server.**
{% endhint %}

Use the following command to run both the server and the client. Use this from now on any time you want to run your application.

```
npm run dev
```

Your browser window will open automatically and you can click on signup and [create an account](/gravity-server/installation/stripe-setup).

### 4. Clean Up

{% hint style="danger" %}
You **MUST** remove the setup files when you have completed the steps above. Failing to do so will let anyone access the setup process.
{% endhint %}

You can use the cleanup script to automate this for you:

```javascript
npm run cleanup
```

If you want to remove the files manually:

* /client/src/views/setup folder
* /server/controller/setupController
* /server/model/setup
* remove the setup import from /server/api/index.js
* the setup route import in /client/src/app.js

{% hint style="warning" %}
It's recommended that you run **npm audit** in each installation folder to ensure third-party packages are up-to-date and secure.
{% endhint %}


# Application Structure

Gravity follows a simple **Model–View–Controller (MVC)** pattern with a REST API.

This keeps your codebase predictable, easy to extend, and easy for AI tools to work with.

#### By Architecture

**Next.js** \
\
The MVC pattern is implemented within a single app:

* **Models** → Database schemas and data logic
* **Controllers** → Server actions / API route handlers
* **Views** → React components (pages + UI)

Routing and backend logic are co-located, removing the need for a separate server.

***

**Node.js**\
\
Gravity Server is structured as a traditional MVC backend:

* `server.js` → Entry point of the application
* `api/` → Route definitions (maps requests to controllers)
* `controllers/` → Business logic
* `models/` → Database schemas

Client applications (Web / Native) communicate with this server via REST APIs.

## Controllers

Controllers are located inside the `/controller` directory, the following come as standard:

* accountController
* aiController
* authController
* demoController
* eventController
* feedbackController
* inviteController
* jobController
* keyController
* pushtokenController
* setupController
* socialController
* userController
* utilityController

## Models

Models are located in the `/model` directory, and the following are included for you:

* account
* auth
* demo
* email
* feedback
* invite
* key
* knex
* log
* login
* mongo
* openai
* pushtoken&#x20;
* setup
* stripe
* token
* user

## Helpers

Helpers are located in the `/helper` directory, and the following are included for you:

* chart
* file&#x20;
* mail
* notification
* s3
* utility

## Views

Views are where your UI components and live. Their location depends on your architecture and client:

**Next.js**&#x20;

* Located inside: `/src/app`
* Pages, layouts, and React components live alongside server actions and API routes
* Allows co-locating frontend and backend logic for faster development and simpler imports

**Gravity Web**&#x20;

* Located at: `/client/src/views`
* Contains web-specific pages and UI components

**Gravity Native**&#x20;

* Located at: `/app/views` inside your Gravity Native project folder
* Contains mobile screens and components

{% embed url="<https://www.youtube.com/watch?v=dEXQErxl2oI>" %}


# REST API

The API files are located in the `/api` folder in the Node.js version and */src/api* in the Next.js version.

The structure of these files is simple; there is a list of endpoints that connect directly to the relevant controller method.&#x20;

```javascript
// node.js
api.post('/api/account', use(accountController.create));

// next.js
export const POST = withApiRoute(accountController.create);
```

Each controller call is wrapped in a HOC (higher-order component). This is a middleware function that catches any errors in the controller methods and then passes these to a global [error handler](/gravity-server/handling-errors) – this prevents you from having to use `try...catch` in your application.

## Protected Routes&#x20;

You can protect any API route and make it accessible to only a specific user level or API key scope using the `auth.verify` middleware metho&#x64;**.** You simply pass the user permission as the first argument, and an optional API scope as the second.

```javascript
// protect using a user permission
api.get('/api/user', auth.verify('user'), use(userController.get));

// protect using a user permission and api scope
api.get('/api/user', auth.verify('user', 'account.read'), use(userController.get));

// next.js
export const GET = withApiRoute('owner', 'user.read', userController.get);
```

Learn more about [API scopes](/gravity-server/rest-api/api-scopes) and find out more about how authentication works in the [next section.](/gravity-server/authentication)&#x20;

## Accessing The API

You can access the API using one of two methods:

1. Using a Bearer token issued during the [authentication](/gravity-server/authentication) flow
2. Using an API key&#x20;

### Using Bearer Tokens

Bearer tokens are JWTs that are used to authenticate the user. You can use a Bearer token with the API by passing it in the `Authorization` header.

```javascript
Bearer your_jwt
```

### Using API Keys

API keys can be created by either an owner or developer permission and can be used to access any endpoint that has an API scope (see Protected Routes above).&#x20;

{% hint style="warning" %}
API keys are stored in the database in plain text to remove the decryption overhead on each API call, and so the user can retrieve an API key if they lose it. \
\
For most applications this is acceptable, if your database is breached and the API keys are stolen, an attacker already has access to all of your data, so encryption provides minimal protection in this scenario.&#x20;
{% endhint %}

The following example demonstrates how to make an API request in Javascript with the Axios package and Basic authentication.&#x20;

```javascript
const res = await axios({

  url: 'https://yourdomain.com/api/user'
  method: 'POST',
  data: {
    email: 'kyle',
    password: 'test1'
  },
  headers: {
    Authorization: 'Basic YOUR_API_KEY'
  },
});
```

## Input Validation

Input validation is handled with JOI, which has been wrapped in a HOF to support [locales](/gravity-server/localization).&#x20;

```javascript
// validate
const data = utility.validate(joi.object({
    
    email: joi.string().email().required(),
    name: joi.string().required().min(3).max(100),
    password: joi.string().required().pattern(new RegExp(config.get('security.password_rules'))),
    confirm_password: joi.string().allow(null),
    verify_view_url: joi.string().allow(null)

}), req, res);

```

## Rate Limiting

API requests are globally rate-limited as defined by the `throttle` settings in [config](/gravity-server/config). The following end points have their own lower rate limits for security purposes:

* POST /api/account
* POST /api/user
* POST /api/user/auth
* POST /api/user/password/reset/request
* POST /api/user/password/reset

## API Logs

Every API request is logged in the `log` table by default. You can toggle this on or off using the `ENABLE_API_LOGS` environment variable.

```javascript
ENABLE_API_LOGS=true
```

## Swagger File

There is a swagger file included in `api/spec.yaml` with documentation and examples for each endpoint using the OpenAPI 3.0 spec. You can import this into Postman for easy testing.


# API Scopes

Every user-generate API key offers granular access control using API scopes. The following scopes are included by default and housed in the [config file](/gravity-server/config) and you can extend this with your own scopes.

* account.read&#x20;
* account.update
* account.delete
* billing.read
* billing.update
* invite.create
* invite.read
* invite.delete
* key.create
* key.read
* key.update
* key.delete
* user.read
* user.update
* user.delete
* event.create
* job.create
* job.read
* job.update
* job.delete


# Webhooks

Gravity has API routes and a `webhookController` pre-configured for you to start accepting webhooks from external services and APIs.

A Stripe webhook handler is included for you and is used to handle accounts [downgrading their plan to free](/gravity-server/free-accounts#downgrading-to-free) at the end of their current billing cycle.&#x20;

Please refer to the Stripe documentation for the latest information on testing [Stripe webhooks](https://docs.stripe.com/webhooks).


# Authentication

The sign-in authentication process is managed in the `authController`.

This method checks that:

* the user exists
* the correct password has been provided
* the account is active&#x20;
* the sign-in is not suspicious (see sections below for more information)

If these conditions are met, an auth token is generated and returned to the client along with a user object.

### Authentication Model

The authentication model is located in `/model` directory and contains several methods for encoding and decoding the JSON web token and also the [verify middleware](/gravity-server/rest-api) for protecting the API endpoints.

## Magic Sign-in Links

Users can sign in using their username or password, or via a magic link that sends a time-sensitive JWT for authentication.

## Suspicious Sign-In Attempts

Each sign-in attempt is stored in the login table along with the device, browser and IP address. On each login attempt, `authController.signin` checks this table for suspicious activity based on past behaviour. \
\
If the IP address, device or browser differs from what the user typically uses to sign in, they will be notified via email.

## Blocked Sign-In Attempts

If all three parameters (IP,  browser and device) differ from past behaviour. The user's account will be disabled and the sign-in attempt blocked. The user will then receive a magic sign-in link via email to sign-in and unlock their account.

## Check the Auth Status

The auth status of a user can be checked by making a GET request to `/api/auth`**.** This request is performed every time the app is loaded or reloaded.

This will return an object with the following values:

| Key                         | Value         | Description                                                             |
| --------------------------- | ------------- | ----------------------------------------------------------------------- |
| jwt\_token                  | true or false | determines if the user has an active JWT                                |
| <p></p><p>social\_token</p> | true or false | determines of the user has an active access token from a social network |
| subscription                | string        | returns the stripe subscription status                                  |
| accounts                    | array         | a list of the account IDs the user belongs too                          |
| account\_id                 | UUID          | the currently authenticated account id                                  |
| authenticated               | true or false | true if the user has an app JWT or social token                         |

## **Deleting Auth Tokens**

You can sign out the user and delete the auth tokens by making a DELETE request to `/api/auth`


# Email Verification

Email account verification is enabled by default. After signing up, a user will be asked to verify their email using a time-sensitive link sent to their registered email address.

Until verified, the JWT token issued to a user will contain an `unverified` flag, and access to protected API endpoints will be disabled.&#x20;

You can override this behaviour by passing an `unverified` permission to an API route as the third parameter.

<pre class="language-javascript"><code class="lang-javascript"><strong>// node.js
</strong><strong>api.get('/api/account', auth.verify('owner', 'account.read', 'unverified'), use(accountController.get));
</strong><strong>
</strong><strong>// next.js
</strong>export const GET = withApiRoute('owner', 'account.read', accountController.get, { allowUnverified: true });
</code></pre>

When a user verifies their account by making a POST request to `/api/user/verify` a new JWT token will be issued that does not contain an unverified flag, unlocking the API access.

### Disable Email Verification

To disable the default behaviour and automatically verify all new users, you can set the following config flag to false:

```javascript
"email": {
  "user_verification": false
 }
```


# Social Sign On

In addition to enabling your users to sign on with their email and password, Gravity supports signing in with social networks using [Passport.js](http://www.passportjs.org) (Node.js version) or [Arctic](https://arcticjs.dev/) (Next.js version).

## Configuring Facebook

In order to sign in with Facebook, you will need to create an app in the [Facebook developer portal](https://developers.facebook.com/apps) and add your app ID and secret to the .env file (you'll be asked to do this during setup, or you can add it manually later).

Please [follow the Facebook documentation](https://developers.facebook.com/docs/development/create-an-app) for the latest guidance on how to do this.

## Configuring Twitter

As with Facebook, you will need to create an app in the [Twitter developer portal](https://developer.twitter.com/apps) and add your app ID and secret to the .env file.

Please [follow the Twitter documentation](https://developer.twitter.com/en/docs/apps/overview) for the latest guidance on how to do this.

## Callback URLs

The default callback URLs are defined in the config file, and follow this structure:

```javascript
http://localhost:8080/auth/facebook/callback
```

You will need to add the callback URL to the authorised endpoints in the developer portal for your chosen social network.

## Disable the Social Sign-Ons

If you'd like to disable social sign-ons, remove the `<SocialSignin/>` component from the auth views in the client to remove the buttons and then disable the API endpoints.

## Using Social Sign Ons with Gravity Native

To use social sign-ons with Gravity Native on your mobile device, you will need to install ngrok instead of using localhost in your callback URLs.

Please [follow the getting started guide on ngrok](https://ngrok.com/docs/getting-started/) to set it up.

{% hint style="warning" %}
Please update the `callback_url` inside the `/config` folder files and **ALSO** the `baseURL` value inside `app/config.json`
{% endhint %}


# Two-Factor Authentication

Users can enable two-factor authentication for their accounts in the `/account/2fa` vie&#x77;**.** Once enabled, the user will be presented with a QR code that they can scan using their authenticator app of choice, such as Google Authenticator.

{% hint style="info" %}
The user's secret (and QR code) is shared across all of their accounts, they don't need to scan a new code for each account they belong to or own. If 2FA is disabled and then re-enabled, they will need to scan the new QR code.
{% endhint %}

2FA works with all the Gravity login flows:

* username and password
* magic links
* social sign-ons

Once a user has signed has completed the first-factor authentication using one of these methods, they will be prompted to enter OTP (verification code) from their authenticator app. This screen has a time-sensitive token (5 mins) created during the first step; this prevents a user from bypassing the first step in the auth flow without a token.

### Setting Your App Name In The Authenticator Apps

To show your application name in the user's authenticator app, simply set the `APP_NAME` env var to the name of your application.


# Authorization

You can restrict access to features based on the users plan or role.


# Feature Access and Plan Restrictions

The most common authorisation scenario you will find yourself building is controlling access to features based on the account's billing plan.

### Users without a plan

By default, users who have not signed up for a plan will only have access to authentication and account profile pages. This ensures that users without an active plan cannot access features beyond the basics.\
\
On the client side, users are restricted to just authentication and profile management until they choose a plan.

On the server side, actions such as inviting child users, accessing AI endpoints, or creating API keys should be disabled for users without an active plan. For example:

```javascript
// check account has a plan
const accountData = await account.get({ id: req.account });
utility.assert(accountData.plan, res.__('account.plan_required'));
```

As you build custom endpoints for your own features, it’s important to include this check to ensure actions cannot be performed without an active plan via the API.

### Restricting features by plan

When you want to limit feature access or impose usage limits based on the user's billing plan, it's recommended to define plan-specific flags or limits in your configuration within the Stripe plans object.

<pre class="language-javascript"><code class="lang-javascript">"plans": [
 {
  "id": "free",
  "name": "Free",
  "type": "free",
  "price": 0,
<strong>  "max_gb": 2,
</strong>  "store_files": true,
 }
]
</code></pre>

In your controller methods, you can check whether the user’s current plan permits the requested action or feature. For example:

<pre class="language-javascript"><code class="lang-javascript"><strong>exports.fileController.save = async function(req, res){
</strong><strong>
</strong><strong> const accountData = await account.get({ id: accountID });
</strong> const currentPlan = settings.plans.find(x => x.id === accountData.plan);
 utility.assert(currentPlan.store_files, res_('file.save.not_permitted_on_plan')

}
</code></pre>

This ensures that the feature or action is only available to users on the appropriate plan.


# Permissions (Roles)

User permission levels are defined in [/config](/gravity-server/config) inside the permissions object. Here you can define a multi-level tier of user access levels.

### Default Permissions

Out-of-the-box, Gravity includes **master** (for [Mission Control](/mission-control/introduction)), **owner**, **admin,** **user** and developer roles.

### Client Permissions

The same permission object is included inside **client/src/permissions.json.** You must use both files, as the React client runs independently to the server. You can also define different permissions for the client UI if you need to.

## How Permissions Work

The permission is stored in the JWT, so you can verify user actions at the API level based on this permission.

The permission level is also stored in the UI context, so you can show or hide features depending on the user's permission level.&#x20;

{% hint style="danger" %}
You should only use the permission stored in the client context for visceral purposes and **ALWAYS** use the API to control access. Savvy users can modify this permission and reveal hidden UI features. Using the API will prohibit them from performing an action they do not have permission to do.
{% endhint %}

## What Each Permission Can Do

Below is a breakdown of what each user permission can do by default. You can customise this logic to suit your own requirements.

Each account can only have one owner but as many admins, users and developers as you need.

### Master

* view all accounts and all users
* edit any account and any user
* view application logs
* view user feedback

### Owner

* can edit billing details
* can close the account
* can invite admins, users and developers
* can promote a user to admin
* can demote an admin to user
* can edit admins, users and developers
* can create, update and delete API keys

### Admin

* can invite users
* can edit users
* can promote a user to admin

### User

* can view/edit/delete data that they are permitted to

### Developer

* everything a user can do plus update and delete API keys


# Config

Configuration settings are stored within a JSON object located inside the `/config` folder. You can add multiple config files here for each development environment that you're using.

Gravity ships with `default` and `production` config files to get you started.

Using this approach will automatically load the correct configuration file depending on your environment. For example, you can load your test Stripe settings in your local development environment and your live settings when running in production.

{% hint style="info" %}
Use a filename that matches your `NODE_ENV` name to load that config file in that environment.
{% endhint %}

### Importing The Config Files

To access the key-value pairs in the config file within your code, you first need to import the config file.&#x20;

First, import the `config` package and then `get` the specific object you want from the config file.

```javascript
const config = require('config');
const settings = config.get('stripe');
```

This will import the Stripe settings from the config file loaded in your current environment.

{% hint style="danger" %}
You should store sensitive items like passwords and secrets inside an [environment variable](/gravity-server/environment-variables) instead of the config file.
{% endhint %}


# Environment Variables

Sensitive information like API keys and tokens are stored inside the `.env` file.

When deploying your application, you should ensure these values are set up in your production  environment and ignore the .env file included with Gravity, as this is for development purposes only.&#x20;

Below is a list of the variables used in Gravity. Required variables will be populated for you during the setup process.

```properties
STRIPE_SECRET_API_KEY=
MAILGUN_API_KEY=
CLIENT_URL=http://localhost:3000
WEBSITE_URL=http://localhost:4000
MISSION_CONTROL_CLIENT=http://localhost:5002
TOKEN_SECRET=
CRYPTO_SECRET=
SESSION_SECRET=
SUPPORT_EMAIL=
GENERATE_SOURCEMAP=false
INLINE_RUN_CHUNK=true
FACEBOOK_APP_ID=
FACEBOOK_APP_SECRET=
TWITTER_API_KEY=
TWITTER_API_SECRET=
AWS_ACCESS_KEY=
AWS_SECRET_ACCESS_KEY=
S3_REGION=
S3_BUCKET=
PRODUCTION_DOMAIN=
DB_USER=
DB_PASSWORD=
DB_HOST=
DB_CLIENT=
DB_NAME=
DB_PORT=
ENABLE_API_LOGS=true
STORE_EVENT_LOGS=true
APP_NAME=Gravity
OPENAI_API_KEY=
REDIS_JOB_URL=
```


# Database Queries

Before executing a database query, you must import the database model into the file where you will perform the query (usually your model file).

```javascript
// sql
const db = require('./knex')();

// mongo
const mongoose = require('mongoose');
```

### Performing Database Queries&#x20;

You can perform a query using [Knex](https://knexjs.org) or [Mongoose](https://mongoosejs.com).

```javascript
// knex
return await db('user').select('*').where({ account_id: account });

// mongoose
return await User.findOne({ account_id: account });
```


# Handling Errors

Each [API](/gravity-server/rest-api) controller method call is wrapped in a HOF (higher-order function) called `use`**.**

```javascript
// node.js
api.post('/api/account', use(accountController.create));

// next.js
export const GET = withApiRoute('owner', 'account.read', accountController.get);
```

This is a middleware function that catches any errors in the controller methods and then passes these to a global error handler. This prevents you from having to use `try...catch` in your application.

When an error is caught, it will be logged to the console and a 500 status message returned to the client along with the error message.&#x20;

{% hint style="info" %}
Errors are [automatically logged](/gravity-server/logging) and accessible any time in [Mission Control](/mission-control/introduction).
{% endhint %}

Please refer to the error handling section in [Gravity Web](/gravity-web/handling-errors) or [Gravity Native](/gravity-native/handling-errors) to understand how these errors are handled on the client side.&#x20;


# Logging

Gravity comes with a built-in logging tool that logs errors by default and allows you to record your own custom logs.

```javascript
log.create({ message, body, req, sendNotification, user, account });
```

You can pass 6 parameters to the log model:

| Parameter        | Type                 | Description                                                                                       |
| ---------------- | -------------------- | ------------------------------------------------------------------------------------------------- |
| message          | string               | description of log                                                                                |
| body             | json or error object | full error or response object                                                                     |
| req              | request object       | the model will extract the URL, HTTP method, **user\_id** *and* **account\_id** from the request. |
| sendNotification | true or false        | Determines if an email should be sent to you as soon as an error occurs (default is false)        |
| user             | string               | user ID to override the request object (or if not present in request)                             |
| account          | string               | account ID to override the request object                                                         |

### View Application Logs

You can see all of your application logs inside the [logs section of Mission Control](/mission-control/logs).


# Localization

Gravity ships with localization support and two languages (English and Spanish) out of the box. This section will example how to use localization on the server side.

{% hint style="info" %}
Refer to the [client-side localisation section](/gravity-web/localization) to learn how to translate the UI.
{% endhint %}

Localization is managed with the i18n package, which can be used to translate individual requests depending on the language provided by the client using the `Accept-Language` header.

## Locale Files

Locale files are stored inside the `/locales` folder, and each language has its own folder of `.json` files. The locales are split up based on entities in the application to match the same structure as the models, views and controllers.

All `.json` files inside each locale folder are automatically imported and combined using the i18n `helper.config()` method, so you can add more JSON files without explicitly importing them anywhere.

```
/locales
  /en
    en_account.json
    en_user.json
  /es
    es_account.json
    es_user.json
```

The locale files are simple JSON files containing the strings. The keys are always the same (in English) as these are referenced in the code. The string changes depending on the language.

```javascript
// en_account.json
{
  "create": {
    "denied": "Registration denied",
    "duplicate": "You have already registered an account",
    "duplicate_child": "You already have an account registered with this email address. Please enter your original password to continue"
}

// es_account.json
"create": {
  "denied": "Registro denegado",
  "duplicate": "Ya has registrado una cuenta",
  "duplicate_child": "Ya tienes una cuenta registrada con esta dirección de correo electrónico. Por favor, introduce tu contraseña original para continuar"
  },
```

### Adding More Locales

{% hint style="info" %}
Pro tip – ask [ChatGPT](https://chat.openai.com/) to translate the JSON files to new languages.
{% endhint %}

1. Create a new folder inside /locales eg. `/fr`
2. Add the new locale to the locales array in `/helper/i18n`

```javascript
i18n.configure({

  defaultLocale: 'en',
  locales: ['en', 'es', 'fr'], // add new locale here
  updateFiles: false,
  objectNotation: true,
  staticCatalog: translations

});
```

## Performing Translations

Translations can be performed in two ways:&#x20;

1. Using the i18n package and setting the locale
2. Using the `res` object.

```javascript
// translate
const i18n = require('i18n'); 

function translateWithi18n(req, res){

 // translate using the i18n package
  i18n.setLocale(req.locale);
  return res.status(200).send({ message: i18n.__('account.plan.updated'));

}

function translateWithRes(req, res){
    
  // translate using res
  return res.status(200).send({ message: res.__('account.plan.updated'));

}

```

You can use the `locale` column on the user table when neither option is available, for example, in a background job worker.

### Translating Controllers

To translate inside a controller method with the `res` object available, use the `res.__` method.

### Translating Models & Helpers

If the `res` object is unavailable - i.e. you're translating a model or helper file, you can pass the `req.locale` var to the method.

```javascript
function controller(req, res){

  convertToMonthName(2, req.locale);
  
}

function convertToMonthName(month, locale){

  locale && i18n.setLocale(locale);
  const monthNames = i18n.__('global.months').split(',')
  return monthNames[month-1];

}
```

## Handling Plurals

The `i18nHelper` has a method for handling plurals for you. You just need to structure your JSON as follows, and pass the translation key and number to the helper.

<pre class="language-javascript"><code class="lang-javascript"><strong>{
</strong> "sent": {
   "one": "Invite sent",
   "other": "Invites sent"
  }
}

<strong>i18nHelper.plural('invite.sent', emails.length);
</strong></code></pre>

## Emails

Emails are handled slightly differently because the content is stored in the database, not the locale files. Each email in the `email` table has a `locale` column so the content can be localised. You pass the `locale` as part of the data prop to the mail method.

```javascript
await mail.send({

 to: userData.email,
 locale: req.locale,
 template: 'new_account'
 
});

```


# Push Notifications

If you purchased a Gravity Native or Gravity Power plan, you can send push notifications from the server to mobile devices on iOS or Android.&#x20;

When a user provides permission in-app to send push notifications, a push token for their device is stored in the database.

## Sending Push Notifications

To send a notification, simple call the **send** method of the notification helper and pass an array of tokens, plus the message you want to send.

```javascript
const notification = require('./helper/notification')

const tokens = ['wX1JVaQrCXOdTgAHa3FP', 'bXzeupkfVIKlrIKwtOH2']

notification.send(tokens, {

  title: 'New User',
  body: 'A new user has just signed up to your app',

})
```

{% hint style="info" %}
You can also pass data and sounds, please [refer to the Expo documentation](https://docs.expo.io/push-notifications/overview/) for more information.
{% endhint %}

You will probably only ever send one-off notifications based on a user's actions, however if you need to send hundreds of messages simultaneously, you may want to consider [bulk sending.](https://github.com/expo/expo-server-sdk-node)

## Removing Tokens

{% hint style="danger" %}
Failing to stop sending notifications to an unregistered device may result in being banned from sending push notifications by Apple and/or Google.
{% endhint %}

If a user disables push notifications for your service at any point, Gravity will remove the device token from the database the next time you attempt to send a notification and there is a `DeviceNotRegistered` error.


# Email Notifications

You can send email notifications to your users from anywhere in your application.

Emails use a JSON template for content - no need to wrestle with HTML tables. The JSON is then injected into an email template located in the `/emails` directory.

```javascript
await mail.send({

 to: 'name@email.com,
 template: 'welcome',
 content: {
  
  name: 'John',
  plan: 'startup',
  price: '$49'
  
 }
});
```

You can also use a custom HTML template by passing the file name as a `custom` value.

## Sending Notifications to Yourself

If you wish to send a notification to yourself, you can use the mail utility endpoint:

```javascript
POST /api/utility/mail

// params 
{
  name: 'from-name',
  email: 'from-email-address',
  message: 'message body'
}
```

This will send an email to the address stored in `SUPPORT_EMAIL` [environment variable](/gravity-server/environment-variables). You can see an example of this in the /help view.&#x20;

## Using Other Mail Providers

Gravity uses [nodemailer](https://nodemailer.com/) with the default mail service set to [Mailgun](httsp://mailgun.com).&#x20;

If you wish to use another mail provider, you can simply install the nodemailer transport package for your chosen service and update the `mail.send` method in `helper/mail`**.**

1. Find and install the transport package from [npm](https://www.npmjs.com/)
2. Change the require import on line 7 of **helper/mail** to import your package
3. Update the authentication object in line 25 of **helper/mail** to match your service's requirements

```javascript
const mailgun = require('nodemailer-mailgun-transport'); // require transport

exports.send = async function(data){

  // transport auth
  const transport = nodemailer.createTransport(mailgun({
    host: settings.host,
    auth: {
    api_key: process.env.MAILGUN_API_KEY,
    domain: settings.domain
  }))
}
```

A f[ull list of well-known services is available here.](https://nodemailer.com/smtp/well-known/)

## Using Custom Email Templates

Gravity includes a clean, responsive email template, but you can also add your own templates to cover a wider variety of use cases.

{% hint style="info" %}
I recommend using [htmlemail.io](https://htmlemail.io) for premium templates. **Gravity customers get 20% OFF** – you’ll receive a coupon via email after you purchase.
{% endhint %}

For full instructions on how to implement a custom template, [please follow the instructions in this blog post](https://usegravity.app/blog/how-to-use-custom-email-templates-with-gravity).

{% hint style="info" %}
If you're migrating from a previous version of Gravity, please run the seeds below to populate the database with the email content.
{% endhint %}

```javascript
knex seed:run // sql
node seed/mongo // mongodb
```

## Notification Preferences

Users can't toggle which email notifications they would like to receive in the *notifications* section of their account.

These settings are stored in the database notifications table. By default, the following preferences are  included for you:

* new\_signin
* plan\_updated
* card\_updated
* invite\_accepted

If you'd like to add more options you, add them to the `notifications` object in [config](/gravity-server/config). This way, all new users will automatically have the these preferences added to the database.


# User Feedback

You can collect user feedback directly within Gravity without having to use any external tools or services. Simply import the [\<Feedback/>](/gravity-web/components/feedback) component into any page where you'd like to collect feedback.&#x20;

You can view, delete or respond to user feedback within [Mission Control](/mission-control/feedback).&#x20;


# User Onboarding

A sequence of user onboarding automations that are performed as a background job are included.

The following emails are sent to a new user:

1. Verification email
2. Verification reminder after 1 day if not verified&#x20;
3. Welcome email when verification is completed
4. Subscription plan selected
5. Trial expires in 3 days
6. Trial has expired and account was upgraded

{% hint style="warning" %}
The link in the verification reminder email only works with the Gravity web client. Verification requests for the native app must be triggered via the 'resend verification' button within the app.
{% endhint %}

## Starting The Onboarding Worker

There is a background job worker that runs the onboarding flow once every day at 12:05pm London time. You can adjust this inside the [config](/gravity-server/config) by changing `worker_schedule.onboarding`.

This flow performs 3 tasks out-of-the-box:

1. Gets a list of active trials from Stripe and sends an email to those expiring in 3 days from today.
2. Gets a list of trials from Stripe that expire today and notifies the user they have been upgraded to the paid plan they selected.
3. Gets a list of new accounts created yesterday that are still unverified and sends the user a reminder email.

Ensure the background worker is running and then execute the start script to begin the daily CRON job.

```javascript
node worker/onboarding // start the background job worker
node worker/onboarding/start // start the daily cron queue
```


# File Uploads

You can upload files via the [React form](/gravity-web/components/form) using input type of `file`**.** The file component supports multiple file uploads using a drag-and-drop interface.

{% hint style="info" %}
[User avatars](/gravity-web/components/user) demonstrate a full working example of uploading files from the client and storing them in an S3 bucket.&#x20;
{% endhint %}

<pre class="language-javascript"><code class="lang-javascript">&#x3C;Form inputs={{ 
  avatar: {
<strong>    label: 'Profile Picture',
</strong>    type: 'file', 
    required: false, 
    max: 1,
 },
}}/>
</code></pre>

Uploads are handled on the server using [multer](https://www.npmjs.com/package/multer) and stored in the `/uploads` directory.

There is a utility API endpoint for uploading files.

```javascript
/api/utility/upload
```

When using this endpoint, a temp file will be stored in the `/uploads` folder by multer. The controller will then upload this file to S3 to your default bucket.

Alternatively, if you want to upload a file to another endpoint, you'll need to use the multer middleware in the same manner as the utility endpoint.

```javascript
const multer = require('multer');
const upload = multer({ dest: 'uploads' });
api.post('/api/utility/upload', upload.any(), use(utilityController.upload));
```

## Uploading Files to Amazon S3

Gravity includes a helper for interacting with S3, so you can manage your S3 buckets and files in a few lines of code.

### 1. Add AWS Credentials to .env

You need to add the following three credentials to your environment to use the S3 helper:

```
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_REGION=
```

### 2. Import The Helper

Import the helper anywhere in your project and call one of the helper methods.

```javascript
const s3 = require('/helper/s3');

// list the buckets
await s3.bucket();

// list the items in the bucket
await s3.bucket.items(bucketName);

// create a new bucket
await s3.bucket.create(bucketName);

// delete a  bucket
await s3.bucket.delete(bucketName);

// upload a file or buffer and return the S3 url
await s3.upload({ bucketName, file, buffer, acl });

// delete a file using a filename or S3 url
await s3.delete({ bucketName, file, url });

// get a signed url for a file
await s3.signedURL({ bucketName, fileName, expires, acl });
```


# Billing

Gravity comes with subscription payments out-of-the box using [Stripe](https://stripe.com).&#x20;

Users enter their credit card when they sign up and will be billed each month. To allow users to sign up **without** a credit card, please see the section on [free accounts](/gravity-server/free-accounts).

[Seat billing](/gravity-server/payments/seat-billing) and [usage billing](/gravity-server/payments/usage-billing) are also supported out-of-the-box.

## Trials

If you'd like to offer a trial period before a user's card is charged, add a trial\_period\_days value to the Stripe plan in [config](/gravity-server/config).

```javascript
trial_period_days: 5
```

This is only applied when a user first signs up and selects a plan, not when they upgrade to a paid plan. You can enable this functionality by passing trial\_period\_days to the `stripe.customer.subscribe` method.

## Failed Payments

If a card payment fails, Stripe will retry to charge the card as per your [subscription settings](https://dashboard.stripe.com/settings/billing/automatic) and email the customer. If a customer needs to update their card details, Stripe will include a link to your billing settings.

{% hint style="info" %}
Configure your billing link in Stripe to: <https://yourdomain.com/account/billing> so the customer can log into your app and update their card.
{% endhint %}

### Handling Cancelled Subscriptions

If all retries have failed, then the subscription is marked as `cancelled`.&#x20;

The default behaviour is to inform the user the next time they sign in and prompt them to create a new subscription or choose the free plan.

## 3D Secure Authentication

Strong Customer Authentication (SCA) is **included** with Gravity - you don't need to do anything to activate it.

When charging a European card, the customer's bank may ask for further verification. If this happens, a 3D secure pop-up will be displayed and the user can perform the validation in-app.

If a subscription payment requires verification while the user is off-app you should enable the **Send a Stripe-hosted link for cardholders to authenticate when required** option in your [Stripe settings](https://dashboard.stripe.com/settings/billing/automatic).&#x20;

This will send an email to the customer to complete the verification. They will also be directed to the billing view the next time they log into your app and will see a message to check their email and verify the payment.


# Seat Billing

Gravity supports seat (or per-user) billing, so you can charge an account owner based on the number of users on their account.&#x20;

When a new user accepts an invite, they will be added to the subscription for that account. If they are removed from the account they will be removed from the subscription.

It's disabled by default. You can toggle this behaviour on or off in the [config](/gravity-server/config) file:

```json
"seat_billing": false,
```

{% hint style="warning" %}
You should also enable it in the Mission Control config file too.
{% endhint %}

## Enabling Volume Billing in Stripe

Per-seat billing using the volume pricing in Stripe. You need to create a new price and set the pricing model to `volume pricing`.

Then, set the price per unit to whatever you want to charge per seat. Gravity will add or subtract a unit from the subscription when a new user is added or deleted.&#x20;

<figure><img src="/files/fOveFX2AAcYfgBR7j58r" alt=""><figcaption></figcaption></figure>

## Prorations

On seat-tiered plans, `invoice_now` and `prorate` are enabled. Users will be charged at the end of the billing cycle for uncharged tiered usage.


# Usage Billing

Usage billing enables you to bill your customers based on the volume of use in the billing period. For example, if you want to implement a credits-based pricing system, you can do so with usage billing.

Usage is tracked in the usage table and is reported to Stripe daily (customisable).

{% hint style="info" %}
Usage tracking is supported on [free accounts](/gravity-server/free-accounts) but won't be reported to Stripe or billed.
{% endhint %}

## Enabling Usage Billing in Stripe

Usage billing uses the volume pricing in Stripe.&#x20;

You must create a new price and set the pricing model to volume pricing. Then, set the price per unit to whatever you want to charge per unit.

{% hint style="danger" %}
Stripe doesn't support switching from a non-volume pricing model to a volume pricing model on the same subscription. Ensure all of your prices are volume pricing to support changing plans.
{% endhint %}

<figure><img src="/files/fOveFX2AAcYfgBR7j58r" alt=""><figcaption></figcaption></figure>

## Incrementing Usage

To track usage, use the `usage.increment` method. You can increment by a specific amount  passing `quantity` or omit it to increment by one.

```javascript
const usage = require('./model/usage');

// increment by 10 
usage.increment({ account: 'account_id', quantity: 10 });

// increment by 1
usage.increment({ account: 'account_id' });
```

You should call this function in any relevant API endpoint where you want to log use.

## Reporting Usage to Stripe

Usage is [reported to Stripe](https://stripe.com/docs/products-prices/pricing-models#reporting-usage) once per day using the `usage` [background job](/gravity-server/background-jobs). You can customise how often this job runs in `config.worker_schedule.usage`

Shorter billing times are recommended to ensure Stripe always has the latest usage data for the current billing period and can charge your customers the correct amount.&#x20;

When usage is reported, the `report` column of the usage table for the corresponding report will be marked as `true`. The `end_period` will also be populated, and the report is closed.&#x20;

The worker will then open a new report, and any incrementation will be applied to the new open report.

## Getting Usage

You can use the usage.get method to fetch usage. The client application will fetch and display usage for the current billing period.

```javascript
const usageData = await usage.get({ 

  account: 'account_id', 
  period_start: '2023-10-10', 
  period_end: '2023-11-11'

});
```

## Prorations

On usage (and seat) tiered plans, `invoice_now` and `prorate` are enabled. Users will be charged at the end of the billing cycle for uncharged tiered usage.


# Free Accounts

You can offer a free plan to your users' simply by adding a new plan object inside `stripe.plans` in your config file.&#x20;

{% hint style="warning" %}
You can name the plan whatever you like, but the id must be set to `free`
{% endhint %}

```javascript
  {
    "id": "free",
    "name": "Hobby",
    "price": 0,
    "interval": 'month',
    "currency": { "name": "usd", "symbol": "$" }
  }
```

This will bypass the payment and create a user on a free pla&#x6E;**.** You can then restrict features in your app, if required, based on the user's plan.

## Upgrading From Free to Paid

Users on the free plan can upgrade to a paid plan in `account/billing`\
\
If a user is on the free plan and selects a paid plan, the server will return a `402 Payment Required` response, which will redirect the user to a payment form.

{% hint style="info" %}
You can return a 402 status anywhere in your application to force the user to upgrade to a paid plan.
{% endhint %}

Once the payment has been processed, a new Stripe customer and subscription is created for the user.

The user's plan will be updated in client context so they can immediately access the features for that plan on the front end.

## Downgrading to Free

When a user downgrades from a paid to free plan in the billing view, their Stripe subscription will automatically be cancelled at the end of their current billing cycle. They will remain on their paid plan and be able to access the features they paid for until the subscription is cancelled.&#x20;

The cancellation will be handled by a Stripe [webhook](/gravity-server/rest-api/webhooks) for `customer.subscription.deleted`.


# CLI Toolbelt

Adding your own features is as simple as spinning up a new model, view and controller. Gravity comes with a CLI toolbelt that you can use to generate the files needed for this automatically.

## Create a New View

```javascript
gravity create yourModelName -db -ui
```

This will create:

1. a new model template in `/model` directory&#x20;
2. a new controller template in the `/controller` directory that handles the server request and calls the appropriate model method
3. a new set of endpoints in `/api` to route the request to your controller methods
4. a React view in `/client/src/views` that makes the API request

If you don't want to create a database table or a React view then you can omit the `-db` and `-ui` parameters.

## Create a New React Component

You can automatically create a new React component and import it into the component library using the following command in the terminal:

```javascript
gravity create component yourComponentName
```

## Running Unit Tests

The toolbelt can also execute a series of [unit tests](/gravity-server/testing) to test your application rigorously.

```javascript
gravity test
```

## Create a New Master Account

If you want to create a new master account for [Mission Control](/mission-control/introduction), you run the following command:

```
gravity create master yourname@domain.com:YOUR_PASSWORD
```


# Testing

Gravity comes with a full suite of integration tests included to ensure your application functions as it should before releasing it.&#x20;

{% hint style="warning" %}
Please set the **SUPPORT\_EMAIL** variable in your [environment](/gravity-server/environment-variables) before running tests as this email is used for testing.
{% endhint %}

```javascript
// to start the tests, run:
gravity test

// or
npm test
```

The testing suite uses [mocha](https://mochajs.org) and [chai](https://www.chaijs.com), boilerplate tests are in the `/test` folder.&#x20;

## How Tests Work

A test calls the API endpoint and passes a data object. The response is then tested to ensure the correct status is returned and the returned data matches a specific format.

```javascript
describe('POST /account', () => {
  it ('should create a new paid account', done => {

    config.account.paid.token = { id: 'tok_visa' };

    chai.request(server)
    .post('/api/account')
    .send(config.account.paid)
    .end((err, res) => {

      res.should.have.status(200);
      res.body.token.should.be.a('string');
      res.body.plan.should.eq(config.account.paid.plan);
      res.body.subscription.should.eq('active');
      process.env.token = 'Bearer ' + res.body.token;

      // cleanup
      delete config.account.paid.token;
      done();

    });
  }).timeout(config.timeout);
});
```

To add your tests, create a new test script and import it to the `run.js` file; use the example above as a template.

{% hint style="info" %}
Tests are designed to work with `email_verification` enabled. If you modify the config, some tests may fail and need to be adjusted for your own requirements.
{% endhint %}


# AI Tools

Gravity ships with support for generative AI images and text with ChatGPT and Dall-E.

{% embed url="<https://youtu.be/aJYzm0cdGh0>" %}

### 1. Create an OpenAI API Key

You must [register an account with OpenAI](https://platform.openai.com/) and [create an API key](https://platform.openai.com/account/api-keys). Then, add your API key to the .env file in Gravity:

```properties
OPENAI_API_KEY=YOUR_API_KEY
```

### 2. Generating Text with ChatGPT

Import the OpenAI model and make a request using the following:

```javascript
const openai = require('./model/openai');

async function generateText(){
  const textData = await openai.text({ prompt: 'YOUR PROMPT' }});
}  
```

This function will return a text string from ChatGPT.

### 3. Generating Images with Dall-E

Import the OpenAI model and make a request using the following:

```javascript
const openai = require('./model/openai');

async function generateImage(){
  const imageData = await openai.image({ 
  
    prompt: 'YOUR PROMPT', 
    size: '512x512'
    number: 1,
    
  }});
}
```

Size is optional - the default is `512x512` pixels. You can also specify the number of images to return; the default is 1.

This function will return an array of image objects with a url key containing your image.

```javascript
[{ url: 'https://your_generated_image_url', created: timestamp }]
```

### Included Endpoints

There are pre-configured endpoints included for you to use from the client:

```javascript
POST /api/ai/text
POST /api/ai/image
```


# Background Jobs

Gravity supports background jobs with [Bull.js](https://github.com/OptimalBits/bull). These are used for offloading longer tasks to a separate process. There is a working example included that manages an [automated onboarding flow](/gravity-server/user-onboarding).

{% hint style="info" %}
For more information, [please read the Bull docs](https://github.com/OptimalBits/bull).
{% endhint %}

## 1. Add Your Redis URL

You will need to set up a Redis database for tracking the job queue and then add the URL to the following environment variable:

<pre class="language-javascript"><code class="lang-javascript"><strong>REDIS_JOB_URL=
</strong></code></pre>

## 2. Start The Worker

Run the following command to start the background worker:

```
node worker
```

The worker will process jobs added to the queue.

{% hint style="warning" %}
If you're using Mongo, the setup wizard will inject mongo.connect() into the worker. If you bypassed the setup wizard, you need to add this manually.
{% endhint %}

There's also a `Procfile` included to start a separate worker dynos on [Heroku](https://heroku.com) automatically. Other platforms will vary in how you set this up.

The default worker has a `setTimeout` function in `worker/index.js` – you should replace this with your own job function.&#x20;

## 3. Add a Job To The Queue

There are two ways to add a job to the queue in Gravity:

1. Internally
2. Using the API

To add a job on the server, use the following code:

```javascript
const Queue = require('bull');
const jobQueue = new Queue('jobs', process.env.REDIS_JOB_URL);

async function addJob(){

 const job = await jobQueue.add({ /* your custom metadata here */ });
 
}
```

To add a job via the API, make a POST request to:

```javascript
POST /api/job
```

## Updating the Job Progress

It's helpful to set different statuses during the lifecycle of the job:

```javascript
const job = await jobQueue.getJob(id);
job.progress('started');
```

## Updating The Job Data

If you want to update the job metadata, make a PATCH request to:

```javascript
PATCH /api/job/:id
```

The request body will be merged with the existing job data.

## Getting a Job's Status

Once a job has begun, you'll want to check it's status and perform an action when it has completed. To do this, make a GET request to:

```
GET /api/job/:id
```

You can determine if a job has finished using the `finishedOn` key.

You can also fetch a job internally without using the API with:

```javascript
const job = await jobQueue.getJob(id);
```

## Delete a Job

{% hint style="danger" %}
If a job has already started, you can't delete it from the queue.&#x20;
{% endhint %}

You can delete a job that hasn't been executed yet using the the API:

```javascript
DELETE /api/job/:id
```

or anywhere in your internal app:

```javascript
const job = await jobQueue.getJob(req.params.id);
await job.remove();
```


# Deployment

{% hint style="success" %}
[Get $200 of free credits on Digital Ocean.](https://m.do.co/c/e4c24293fe5d)
{% endhint %}

You can deploy the server to any provider that supports Node.js. I recommend using a PaaS service like [Heroku](https://heroku.com) or [Digital Ocean](https://m.do.co/c/e4c24293fe5d) for easy deployments.\
\
Digital Ocean has a much better database service than Heroku.&#x20;

## Using Docker

You can use [this template to deploy with Docker](https://github.com/ctrlTilde/node-api-skeleton), kindly created by [Allan](https://github.com/ctrlTilde).


# Introduction

The Gravity Web client is features a beautiful, [React](https://reactjs.org) user interface built with [Shadcn](https://ui.shadcn.com). This includes a full library of fully-interactive components that you can easily drop into your application, containing everything from [views](/gravity-web/views) and [tables](/gravity-web/components/table) to [self-validating forms](/gravity-web/components/form), [modals](/gravity-web/components/modal) and [notification banners](/gravity-web/components/notification).\
\
Components are styled using Tailwind CSS and SCSS modules; you can use whichever you want to use at the component level.

Building your interface is as simple as creating a new view and adding the pre-built components.&#x20;

The following sections will give you an overview of how React is set up within Gravity, along with an explanation of how to use each of the components.


# Tailwind & SCSS

The default styling in Gravity is Tailwind, but you can add your own custom styling with SCSS if you prefer.

### Using SCSS

Every component supports a custom className prop, so you can create your own SCSS module and pass the styling to the component

```javascript
import { Button } from 'components/lib';
import Style from './dashboard.module.scss';

export function Dashboard(){

  return (
    <div>
      <Button className={ Style.button }
    </div>
  );
}
```

### Using Custom Tailwind Styles

You can feed a custom style to any component by passing it in the `className` prop as per the above example.

## Live Reloading

Live reloading is automatically configured. The `start` script will watch for changes while the `build` script adds the output CSS file to your static build folder.

## Colors

There is also a brand colour configured inside the `tailwind.config.js` file that you can modify with your brand colours.

## Dark Mode

Dark mode is included and can be toggled on or off in the user dropdown nav on the top right corner (click the avatar to open it).

The setting is stored in both the `authContext` on the client and in the user table of the database so it will persist across user sessions.

## Further Help

If you're new to Tailwind and need more help, the [Tailwind documentation](https://tailwindcss.com/docs/utility-first) provides a comprehensive guide to the principles along with a full reference guide to each utility class helper.&#x20;


# Routing

Routing in Gravity depends on your architecture:

**Next.js**

* Routing is file-based inside the `/app` directory.
* Each folder or file in `/app` becomes a route automatically.
* Layouts, server actions, and views can be co-located for simpler imports and faster development.
* Permissions and private routes can be implemented using middleware or wrapper components around page components.
* Adding a new route is as simple as adding a new file.&#x20;

**Node.js** &#x20;

* Routing is handled **client-side** with `react-router-dom`.
* All routes are defined in `/client/src/routes` and should be imported in `/client/src/app/app.js`.

## **Defining a New Route (Node.js)**

Routes are defined in their respective file inside `/client/src/routes`. To add a new route,  import the [View](/gravity-native/components/view) component and add a new object to the route array.

```javascript
  {
    path: '/account/password',
    view: Password,
    layout: 'app',
    permission: 'user',
    title: 'Your Password'
  },
```

Public routes without a permission (such as the auth pages) are generated using the standard  `<Route>` component included with React Router.

```javascript
<Route path={ route.path } element={
  <View display={ route.view } layout={ route.layout } title={ route.title  } />
}/>
```

### Props

| name    | description                   |          |
| ------- | ----------------------------- | -------- |
| title   | page title                    | string   |
| layout  | name of view layout component | string   |
| display | view component with child     | function |

Gravity also contains a  **\<PrivateRoute>** component that enables you to protect routes with a user permission. **\<PrivateRoute>** accepts an optional **permission** prop.

```javascript
<Route path={ route.path } element={
  <PrivateRoute permission={ route.permission }>
    <View display={ route.view } layout={ route.layout } title={ route.title }/>
  </PrivateRoute>
}/>
```

## Code Splitting

If you would like to introduce route splitting to create separate bundles in your application, you can do so by simply lazy loading the view in the route file.

{% hint style="warning" %}
You will need to ensure that your relevant view component is exported as the default export for this to work.
{% endhint %}

```javascript
const Routes = [{
 path: '/dashboard',
 view: lazy(() => import('views/dashboard')),
 layout: 'app',
 permission: 'user',
 title: 'Your Dashboard'
}]
```


# Events

There's no need to deploy expensive third-party analytics tools to track user behaviour in [Gravity](https://usegravity.app) as it comes with client-side event tracking and analytics built in.

## Track Events

To track an event, simply import the `Event` component and call the `create` method with an event `name` and optional `metadata`. This will log a new event in your database.

{% hint style="info" %}
You can toggle the server-side logging using the **`STORE_EVENT_LOGS`** env var.
{% endhint %}

```javascript
import { Event } from 'components/lib';

export function Upgrade(props){

  async function upgradePlan(){
   
     Event.create({ name: 'upgrade', metadata: { plan: 'Unicorn' });
 
  };
}
```

## Standard Events

The following events are already configured for you:

* signin
* selected\_plan
* completed\_onboarding
* cancelled\_onboarding
* upgraded
* invited\_user
* closed\_account

## Event Analytics

You can get a birds eye view of all of your event data in [Mission Control](/mission-control/events). Here you can see:

* the total number of triggers for each event&#x20;
* events charted over time
* a list of all events
* individual event detail

{% hint style="info" %}
Need to see events by a specific user? Search for their email address in the event listing page.
{% endhint %}


# Authentication

Client-side authentication uses a [JSON web token generated on the server](/gravity-server/authentication) that is then passed in each API call from the client to the server.

The token is set to automatically be appended to the header of each API call.

The client auth methods are located within the `AuthProvider` defined in `/src/app/auth.`

The `AuthProvider` handles sign-in, sign-out and checking the user's permissions and active subscription.

The authentication process is:

1. User signs in
2. The server authenticates the user and generates a JWT token
3. The token is returned to the client and `AuthProvider` stores the token
4. When making an API call, the auth token is passed to the server
5. The token is verified on the server

Permissions passed from the server can also be used to create private routes on the client-side using the [**\<PrivateRoute>** component](/gravity-web/routing).


# Localization

Gravity supports client-side localisation with the i18next package. The section explains how to translate the client side UI.

{% hint style="info" %}
Refer to the [server-side localization](/gravity-server/localization) section to learn how to translate the server-side code.
{% endhint %}

## Locale Files

Locale files are stored inside the `/src/locales` folder, and each language has its own folder of `.json` files. The locales are split to match the same structure as the views.

All `.json` files inside each locale folder are automatically imported and combined, so you can add more JSON files without explicitly importing them anywhere.

```javascript
/locales
  /en
    en_dashboard.json
    en_help.json
  /es
    es_dashboard.json
    es_help.json
```

The locale files are simple JSON files containing the strings. The keys are always the same (in English) as these are referenced in the code. The string changes depending on the language.

```json
{
  "title": "Dashboard",
  "message": {
      "title": "Welcome to Gravity!",
      "text": "This is a sample dashboard to get you started. Please read the documentation to learn how to build your own features."
},
{
  "title": "Tablero de Control",
  "message": {
    "title": "¡Bienvenido a Gravity!",
    "text": "Este es un tablero de muestra para comenzar. Por favor, lea la documentación para aprender a construir sus propias características."
},
```

### Adding More Locales

{% hint style="info" %}
Pro tip – ask [ChatGPT](https://chat.openai.com/) to translate the JSON files to new languages.
{% endhint %}

1. Create a new folder inside the `/locales` file with the local name eg. `/fr` and add your JSON files.
2. Import the locale to `app.js` and add it to the `resources` section of the i18n `config`.

```javascript
import French from 'locales/fr/index'

i18n.use(initReactI18next).init({
  resources: {
    en: English,
    es: Spanish,
    fr: French // new language
  }
});
```

## Performing Translations

Translations can be performed in three ways:&#x20;

1. Using the `useTranslation` hook (available anywhere)
2. Using `props.t` (available in all `View` components inside `/views`)
3. Using `ViewContext` (available anywhere)

<pre class="language-javascript"><code class="lang-javascript">import { useContext } from 'react'
import { ViewContext, useTranslation } from 'components/lib'

export function Card(props){

  const { t } = useTranslation();
  const viewContext = useContext(ViewContext)

return (  
  &#x3C;div>
  
    // useTranslation hook
<strong>    &#x3C;div className={ Style.title }>
</strong><strong>     { t('account.card.title') }
</strong>    &#x3C;/div>
    
    // props.t
    &#x3C;div className={ Style.title }>
     { props.t('account.card.title') }
    &#x3C;/div>
    
    // viewContext
    &#x3C;div className={ Style.title }>
     { viewContext.t('account.card.title') }
    &#x3C;/div>
    
 &#x3C;/div>
}
</code></pre>

## Table Translations

To translate the table headers, you can pass the a string reference to the translation object to the `translation` prop of the `<Table/>`. The JSON object should be named `header`.

```javascript
// json
"invoice": {
  "header": {
  "name": "name",
  "key": "key",
  "scope": "scope",
  "active": "active"
  }
}

<Table translation='account.billing.invoice' />
```

## Switching Languages

There is a language switcher component at the top right of the UI for changing the language. You can add more languages to the dropdown in `/components/locale`. You will need to import any additional flags you want to use.

```javascript
import { GB, ES, FR } from 'country-flag-icons/react/3x2'
const flags = { en: GB, es: ES, fr: FR }
```


# Hooks

Gravity has a three React hooks to make your life easier when performing common tasks.

1. [useAPI](/gravity-web/hooks/useapi)
2. [usePlans](/gravity-web/hooks/useplans)
3. [usePermissions](/gravity-web/hooks/usepermissions)


# useAPI

The `useAPI` hook is designed to make API calls and handle errors gracefully. It returns the loading state and the fetched data.

```javascript
const { data, loading } = useAPI(url, method, trigger);
```

The `useAPI` hook returns an object with two properties: `loading` and `data`. You can use the loading value to handle the loading state in the parent object. Data will be returned when the API response is returned.

The hook will automatically forward any errors to the error handler in the `ViewContext`.

### Parameters&#x20;

| param   | description                      | value                   |
| ------- | -------------------------------- | ----------------------- |
| url     | API endpoint URL to call         | url string              |
| method  | HTTP method                      | string **default: GET** |
| trigger | Boolean to re-start the API call | optional                |

### Example

```javascript
import { useState } from 'react';
import { useAPI, Loader, Button } from 'components/lib';

const MyComponent = () => {

  const [trigger, setTrigger] = useState(false);
  const { data, loading } = useAPI('/user', 'GET', trigger);

  return (
    <div>
    
      { loading ? 
        <Loader/> : 
        <p>Data: { JSON.stringify(data) }</p> }
      
      <Button action={() => setTrigger(!trigger)}>
        Refresh Data
      </Button>
      
    </div>
  );
};
```


# usePlans

When you need a list of available Stripe plans, you can use the `usePlans` hook to fetch a list of plans formatted for the UI.

```javascript
const plans = usePlans();
```


# usePermissions

`usePermissions` will return a list from the server for you formatted for use in your UI.

```javascript
const permissions = usePermissions();
```


# Components

Gravity comes packaged with a library of pre-built components to build your own user interface at warp speed without any design skills. Shadcn utilised Radix UI so it's fully accessible to WAGC2 standards.

{% hint style="success" %}
Gravity 12 now uses Shadcn components.&#x20;
{% endhint %}

[Check out the live demo](https://demo.usegravity.app) to see the components in action.

Most of the components from Shadcn are available by default in Gravity. If not, you can install it easily:

```
npx shadcn-ui@latest add component-name
```

### Importing Components

You can import components from a global barrel file rather than having to remember where each component is stored.

```javascript
import { Form, Card } from 'components/lib';
```

{% hint style="warning" %}
Using the barrel file will decrease performance by loading all the components into the bundle.  If you want to avoid this, you can import directly from the component file.
{% endhint %}

Please refer to the following sections for individual instructions on how to use each component.&#x20;

### Figma UI Kit

For prototyping, you can [`download the Shadcn UI kit for Figma`](https://www.figma.com/community/file/1203061493325953101/shadcn-ui-design-system).


# Alert

The `Alert` component displays a callout for user attention with optional `title`, `description`, `icon`, and `button`. It supports different variants for various alert types.

{% hint style="info" %}
Renamed from Message in Gravity 12.
{% endhint %}

### Preview

<figure><img src="/files/HqeBsVh5q9yeAIJQw0oj" alt="Gravity alert component"><figcaption></figcaption></figure>

### Usage

```javascript
import { Alert } from 'components/lib';

function Component({ ...props }){

  return (
    <Alert
      title="Success!"
      description="Your operation was successful."
      variant="success"
      button={{ text: 'Okay', action: () => alert('You clicked me') }}
    />
  )
);
```

### Props

| Prop        | Description                                               | Required | Value                               |
| ----------- | --------------------------------------------------------- | -------- | ----------------------------------- |
| button      | [button](/gravity-web/components/button) object           | optional | object                              |
| className   | custom styles                                             | optional | SCSS or Tailwind object             |
| description | description text                                          | optional | string                              |
| icon        | override the variant [icon](/gravity-web/components/icon) | optional | string                              |
| title       | title text                                                | optional | string                              |
| variant     | alert type                                                | required | string (info/success/warning/error) |

### Example

```javascript
import { Alert } from 'components/lib';

function Example({ ...props }){

  return (
    <div>
      <Alert 
        title='Information'
        description='This is an info alert.'
        variant='info'
      />
      <Alert 
        title='Success'
        description='This is a success alert.'
        variant='success'
      />
      <Alert 
        title='Warning'
        description='This is a warning alert.'
        variant='warning'
      />
      <Alert 
        title='Error'
        description='This is an error alert.'
        variant='error'
      />
    </div>
  )
);

```

### Blank Slate Message

{% hint style="danger" %}
Depreciated in v12.
{% endhint %}

Blank slate messages are used when there is no data to display and prompts the user to take action.

```javascript
<BlankSlateMessage
 title='No items found'
 text='Would you like to create one?' 
 buttonText='Create Item' 
 action={ this.createItem } 
 marginLeft='2em'
/>
```

### Props

| Prop       | Description                       | Required | Value    |
| ---------- | --------------------------------- | -------- | -------- |
| action     | callback executed on button click | optional | function |
| buttonText | button label                      | optional | string   |
| marginLeft | offset the left margin            | optional | string   |
| marginTop  | offset the top margin             | optional | string   |
| text       | message body                      | required | string   |
| title      | message title                     | optional | string   |

### Notes

* The `Alert` component uses the [Icon](/gravity-web/components/icon) and [Button](/gravity-web/components/button) components.
* The `variant` prop determines the styling and default icon for the alert.
* Custom styles can be applied using the `className` prop.
* The `button` prop accepts an object with [button](/gravity-web/components/button) properties to render a button within the alert.
* For more details, refer to the [Shadcn Alert documentation.](https://ui.shadcn.com/docs/components/alert)


# Animate

The `Animate` component is a wrapper component used to animate its children. It supports different animation types and a customizable timeout duration.

### Usage

```javascript
import { Animate } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <Animate type='pop' timeout={ 500 }>
      <div>Content to animate</div>
    </Animate>
  );
}
```

### Props

| Prop     | Description        | Required | Value                                            |
| -------- | ------------------ | -------- | ------------------------------------------------ |
| children | children to render | required | component                                        |
| type     | type of animation  | optional | string (slideup/slidedown/pop), default: slideup |
| timeout  | animation duration | optional | integer, default: 300                            |

### Notes

* The `Animate` component uses the `CSSTransition` component from `react-transition-group`.
* The `type` prop specifies the animation type.
* The `timeout` prop sets the duration of the animation.
* Custom styles for the animations should be defined in `animate.scss`.


# Avatar

The `Avatar` component is an image element with a fallback text for representing the user.&#x20;

### Preview

<div align="left"><figure><img src="/files/G7rDcGdr4ZCrfAiKJmeG" alt="Gravity avatar component" width="94"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Avatar } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <Avatar src='path/to/image.jpg' fallback='KG' />
  );
}
```

### Props

| Prop      | Description                               | Required | Value                   |
| --------- | ----------------------------------------- | -------- | ----------------------- |
| className | custom styles                             | optional | SCSS or Tailwind object |
| fallback  | Fallback text when image is not available | optional | string                  |
| src       | Image source URL                          | optional | string                  |

### Notes

* The `Avatar` component uses `AvatarPrimitive.Root`, `AvatarPrimitive.Image`, and `AvatarPrimitive.Fallback` from `@radix-ui/react-avatar`.
* Custom styles can be applied using the `className` prop.
* The `src` prop specifies the source URL for the avatar image.
* The `fallback` prop provides fallback content when the image is missing.
* For more details, refer to the [Shadcn Avatar documentation.](https://ui.shadcn.com/docs/components/avatar)


# Badge

The `Badge` component displays a badge or a component that looks like a badge. It supports different variants for various badge styles.

## Preview

<div align="left"><figure><img src="/files/7JwNdn3hiUfO383iIm22" alt="Gravity badge component" width="72"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Badge } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <Badge variant='blue'>
      Badge Text
    </Badge>
  );
}
```

### Props

| Prop      | Description   | Required | Value                                                        |
| --------- | ------------- | -------- | ------------------------------------------------------------ |
| className | custom styles | optional | SCSS or Tailwind                                             |
| children  | badge text    | required | string                                                       |
| variant   | badge variant | optional | string (secondary/destructive/outline/red/blue/green/orange) |

### Notes

* The `Badge` component uses the `cn` function from `'components/lib'` to handle class names.
* The `variant` prop determines the styling of the badge.
* Custom styles can be applied using the `className` prop.
* For more details, refer to the [Shadcn Badge documentation.](https://ui.shadcn.com/docs/components/badge)


# Breadcrumb

The `Breadcrumb` component displays the path to the current resource using a hierarchy of links.

### Preview

<div align="left"><figure><img src="/files/xHEOJE7M7kUJwIdAQixP" alt="Gravity breadcrumb component" width="139"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Breadcrumb } from 'components/lib';

function MyComponent({ ...props }){

  const items = [
    { name: 'Home', url: '/' },
    { name: 'Dashboard', url: '/dashboard' },
    { name: 'Settings', url: '/dashboard/settings' }
  ];

  return (
    <div>
      <Breadcrumb items={ items } />
    </div>
  );
}
```

### Props

| Prop  | Description               | Required | Value                                            |
| ----- | ------------------------- | -------- | ------------------------------------------------ |
| items | array of breadcrumb items | required | array of objects ({ name: string, url: string }) |

### Notes

* The `Breadcrumb` component uses the `BreadcrumbList`, `BreadcrumbItem`, `BreadcrumbLink`, `BreadcrumbPage`, `BreadcrumbSeparator`, and `BreadcrumbEllipsis` sub-components.
* The `items` prop specifies the list of breadcrumb items, each containing a `name` and `url`.
* The `BreadcrumbSeparator` component provides a separator between breadcrumb items, defaulting to a right chevron icon.
* The `BreadcrumbEllipsis` component can be used to indicate more items in the breadcrumb path.
* For more details, refer to the [Shadcn Breadcrumb documentation](https://ui.shadcn.com/docs/components/breadcrumb).


# Button

The Swiss Army knife of buttons, the `Button` component displays a button or a component that looks like a button. It supports various styles, sizes, and functionality such as icons, loading states, and navigation.

## Preview

<div align="left"><figure><img src="/files/ZoQcsoXyI0nMF5XW49pw" alt="Gravity button component" width="375"><figcaption></figcaption></figure></div>

## Usage

```javascript
import { Button } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <Button 
      text='Click Me' 
      color='blue' 
      action={ () => alert('You clicked me'); }
    />
  );
}
```

### Props

<table data-full-width="true"><thead><tr><th>Prop</th><th>Description</th><th>Required</th><th>Value</th></tr></thead><tbody><tr><td>action</td><td>callback function</td><td>required if no url prop </td><td>function</td></tr><tr><td>asChild</td><td>render as a slot</td><td>optional</td><td>boolean</td></tr><tr><td>childen</td><td>button label or child component</td><td>optional</td><td>string or component</td></tr><tr><td>className</td><td>custom styling</td><td>optional</td><td>SCSS or Tailwind</td></tr><tr><td>color</td><td>button color</td><td>optional</td><td>string (red/orange/blue/green) default: black</td></tr><tr><td>icon</td><td><a href="/pages/-M-63u_2RmtFuQof8VwI">Icon</a> name</td><td>optional</td><td>string</td></tr><tr><td>iconColor</td><td>icon outline color</td><td>optional</td><td>string (light/dark/green/blue/orange/red) or hex string</td></tr><tr><td>iconFill</td><td>icon fill color</td><td>optional</td><td>hex string</td></tr><tr><td>iconSize</td><td>icon size </td><td>optional</td><td>integer, default: 16</td></tr><tr><td>loading</td><td>toggle loading spinner</td><td>optional</td><td>boolean</td></tr><tr><td>params</td><td>object passed to the callback function</td><td>optional</td><td>object</td></tr><tr><td>size</td><td>Size of the button</td><td>optional</td><td>string (xs/sm/lg/icon/full)</td></tr><tr><td>text</td><td>button label</td><td>required</td><td>string</td></tr><tr><td>type</td><td>button type eg. submit</td><td>optional</td><td>string</td></tr><tr><td>url</td><td>navigate to an internal or external URL</td><td>optional</td><td>string</td></tr><tr><td>variant</td><td>button variant</td><td>optional</td><td>string (destructive/ghost/icon/link/naked/outline/rounded/secondary)</td></tr></tbody></table>

### Example

```javascript
import { Button } from 'components/lib';

function Example({ ...props }){
  return (
    <div>
      <Button 
        text='Primary Button'
        color='blue'
        action={() => alert('Primary Button clicked!')}
      />
      <Button 
        text='Secondary Button'
        variant='secondary'
        action={() => alert('Secondary Button clicked!')}
      />
      <Button 
        icon='search'
        size='icon'
        action={() => alert('Icon Button clicked!')}
      />
    </div>
  );
}
```

### Notes

* The `Button` component uses the `Icon` and `useNavigate` from `'components/lib'` and `Slot` from `@radix-ui/react-slot`.
* The `variant` prop determines the styling of the button.
* Custom styles can be applied using the `className` prop.
* The `color` prop sets the color of the button, defaulting to 'black'.
* The `action` prop is a callback function that gets called when the button is clicked.
* The `loading` prop toggles a loading animation on the button.
* The `url` prop navigates to a specified URL when provided.
* For more details, refer to the [Shadcn Button documentation.](https://ui.shadcn.com/docs/components/button)


# Calendar

The `Calendar` component is a date field component that allows users to enter and edit dates. It leverages the `react-day-picker` library.

### Preview

<div align="left"><figure><img src="/files/lowcPWS7zcRYPF6lpAMp" alt="Gravity calendar component" width="354"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Calendar } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <Calendar />
  );
}
```

### Props

| Prop            | Description                         | Required | Value                  |
| --------------- | ----------------------------------- | -------- | ---------------------- |
| className       | custom styling                      | optional | SCSS or Tailwind       |
| classNames      | classes passed to the day picker    | optional | SCSS or Tailwind       |
| showOutsideDays | show days outside the current month | optional | boolean, default: true |

### Notes

* The `Calendar` component uses the `DayPicker` from `react-day-picker`.
* The `className` prop allows custom styling to be applied.
* The `classNames` prop allows for custom classes to be passed to the day picker.
* The `showOutsideDays` prop determines whether days outside the current month are shown.
* The `components` prop is used to customise the left and right navigation icons.
* See the [Shadcn Calendar docs](https://ui.shadcn.com/docs/components/calendar) for more information


# Card

The `Card` component displays a card with a header, content, and footer. It provides an organized way to present information with optional loading states and alignment features.

## Preview

<div align="left"><figure><img src="/files/CmFBxhXuvKpfBR5dqkHQ" alt="Gravity card component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Card } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <Card title='Card Title' description='Card Description'>
      Card content goes here.
    </Card>
  );
}
```

### Props

| Prop        | Description                                 | Required | Value            |
| ----------- | ------------------------------------------- | -------- | ---------------- |
| center      | align the card in the center of it's parent | optional | boolean          |
| children    | children to render                          | required | component(s)     |
| className   | custom style                                | optional | SCSS or Tailwind |
| description | header description                          | optional | string           |
| loading     | toggle the loading spinner                  | optional | boolean          |
| title       | header title                                | optional | string           |

### Example

```javascript
import { Card, Table, useAPI } from 'components/lib';

function Example({ ...props }){

 const { data, loading } from useAPI('/api/user');

  return (
    <div>
      <Card title='Card Title' description='Card Description' loading={ loading }>
        <Table data={ data }/>
      </Card>
    </div>
  );
}

```

### Notes

* The `Card` component uses `cn` and `Loader` from `'components/lib'`.
* The `center` prop aligns the card in the center of its parent.
* The `loading` prop toggles a loading animation.
* The `children` prop can include a React component(s) or `CardHeader`, `CardContent`, and `CardFooter` components.
* For more details, refer to the [Shadcn Card documentation](https://ui.shadcn.com/docs/components/card).


# Chart

The `Chart` component is a responsive chart that supports multiple datasets and chart types. It leverages the [chart.js](https://www.chartjs.org/) library for rendering various types of charts.

## Preview

<div align="left"><figure><img src="/files/oo20aVESkWwFS25jonqZ" alt="Gravity chart component" width="563"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Chart } from 'components/lib';

function MyComponent({ ...props }){

  const data = {
    labels: ['January', 'February', 'March', 'April', 'May'],
    datasets: [{
      label: 'Dataset 1',
      data: [10, 20, 30, 40, 50],
    }],
  };

  return (
    <Chart 
      type='line'
      data={ data }
      color='blue'
      showLegend
    />
  );
}
```

###

| Prop       | Description                                  | Required | Value                                                           |
| ---------- | -------------------------------------------- | -------- | --------------------------------------------------------------- |
| color      | line color (use array for multiple datasets) | required | string (red/blue/purple/green)                                  |
| data       | chart data                                   | required | [object](/gravity-web/components/chart#data-format) (see below) |
| loading    | toggle the loading spinner                   | optional | boolean                                                         |
| showLegend | toggle the legend                            | optional | boolean                                                         |
| type       | type of chart to display                     | required | string (line/bar/pie/donut/sparkline), default: line            |

## Data Format

Chart data should be in the following format:

```javascript
labels: ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn'],
datasets: [{ label: 'Gravity', data: [3.7, 8.9, 9.8, 3.7, 23.1, 9.0] }]};
```

You can pass multiple datasets to a line or bar chart by adding to the datasets array.

There is a `chart.create` method located inside the `/model` directory on the server that you can use to easily format chart data.&#x20;

```javascript
const labels = 'User';
const data = [
  { label: 'Owner', value: 7233 },
  { label: 'Admin', value: 321 },
  { label: 'User', value: 2101 }
];

return chart.create(data, labels);
```

### Usage

```javascript
import { Chart } from 'components/lib';

function Example({ ...props }){

  const data = {
    labels: ['January', 'February', 'March', 'April', 'May'],
    datasets: [{
      label: 'Dataset 1',
      data: [10, 20, 30, 40, 50],
    }],
  };

  return (
    <div>
      <Chart 
        type='line'
        data={ data }
        color='blue'
        showLegend={ true }
      />

      <Chart 
        type='donut'
        data={ data }
        color='red'
        showLegend={ false }
      />
    </div>
  );
}

```

### Notes

* The `Chart` component uses `LineChart`, `BarChart`, `PieChart`, `DonutChart`, and `SparkLineChart` components for different chart types.
* The `color` prop specifies the color for the chart lines.
* The `data` prop provides the data for the chart in the format `{ labels: [], datasets: [{ label: string, data: [] }] }`.
* The `showLegend` prop toggles the legend display.
* The `loading` prop toggles a loading spinner.
* For more details, refer to the [Chart.js documentation](https://www.chartjs.org/).


# Checklist

The `CheckList` component displays list items with colored checkmarks (✓) or crosses (X). Each item can have a callback function for interaction.

### Preview

<div align="left"><figure><img src="/files/wf3sRZ6sO07oJbk65mxf" alt="Gravity checklist component" width="184"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { CheckList } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <CheckList items={[
      { name: 'Item 1', checked: true, color: 'green' },
      { name: 'Item 2', checked: false, color: 'red' },
    ]} />
  );
}

```

### Props

| Props     | Description                                        | Required | Value                                                            |
| --------- | -------------------------------------------------- | -------- | ---------------------------------------------------------------- |
| className | custom styles                                      | optional | SCSS or Tailwind                                                 |
| items     | list items with checked status, callback and color | required | array \[{ checked: boolean, callback: function, color: string }] |

### Example

```javascript
import { CheckList } from 'components/lib';

function Example({ ...props }) {

  const items = [
    { name: 'Item 1', checked: true, callback: () => alert('Item 1 clicked'), color: 'green' },
    { name: 'Item 2', checked: false, callback: () => alert('Item 2 clicked'), color: 'red' },
    { name: 'Item 3', checked: true, color: 'blue' },
    { name: 'Item 4', checked: false },
  ];

  return (
    <div>
      <CheckList items={ items } />
    </div>
  );
}

```

### Notes

* The `CheckList` component uses the `Icon` and `cn` functions from `'components/lib'`.
* The `items` prop provides an array of objects with `checked` status, `callback` function, and `color`.
* The `className` prop allows for custom styling to be applied.
* Each item can have an optional `callback` function that gets called when the item is clicked.


# Credit Card

The `CreditCard` component displays a visual representation of a credit card, showing the brand, expiry date, and last four digits of the card number.

### Preview

<div align="left"><figure><img src="/files/Zac49HBcIwKK3OtxlmWO" alt="Gravity credit card component" width="314"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { CreditCard } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <CreditCard 
      brand='Visa' 
      expires='12/24' 
      last_four='1234' 
    />
  );
}
```

### Props

| Prop       | Description                     | Required | Value  |
| ---------- | ------------------------------- | -------- | ------ |
| brand      | card provider name              | required | string |
| expires    | card expiry date                | required | string |
| last\_four | last four digits of card number | required | string |

### Notes

* The `brand` prop specifies the card provider name.
* The `expires` prop specifies the expiry date of the card.
* The `last_four` prop specifies the last four digits of the card number.


# Detail

The Detail component displays an organised summary of key/value data pairs, providing a clear and structured view of details.

## Preview

<div align="left"><figure><img src="/files/ECWvE5IsoXclWjSdjP5Z" alt="Gravity detail component" width="367"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Detail } from 'components/lib';

function Example(){

  const data = {
    name: "John Doe",
    email: "john.doe@example.com",
    age: 30
  };

  return <Detail data={ data } show={['name', 'email', 'age']} />;
}
```

### Props

| Prop        | Description                                                  | Required | Value            |
| ----------- | ------------------------------------------------------------ | -------- | ---------------- |
| className   | Custom styling                                               | optional | SCSS or Tailwind |
| data        | Data to display in key/value pairs                           | required | object           |
| show        | Array of keys to show                                        | optional | array            |
| translation | reference to a locale object to use for the key translations | optional | string           |

### Notes

* The `Detail` component uses a table layout to display each key/value pair.
* Keys in the `data` object are converted to a more readable format by replacing underscores with spaces.
* Custom styles can be applied through the `className` prop, using predefined styles from the `detail.tailwind.js` file.
* If the `data` prop is empty or not provided, the component will return `false` and not render anything.
* The translation prop can be used to change the key from the default JSON key.
* Keys can be shown/hidden using the show prop array.


# Dialog

The `Dialog` component is a window overlaid on either the primary window or another dialog window, It can be opened anywhere by calling `context.dialog.open()` with an object containing the necessary parameters.

{% hint style="info" %}
Renamed from Modal in Gravity 12.
{% endhint %}

### Preview

<div align="left"><figure><img src="/files/kkBXLnRgBPxcg7ZmUVHY" alt="Gravity dialog component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { useContext } from 'react';
import { ViewContext, Button } from 'components/lib';

function MyComponent({ ...props }) {

  const viewContext = useContext(ViewContext);

  function openDialog(){
    viewContext.dialog.open({
      title: 'Add User',
      form: MyForm,
      buttonText: 'Send Invite',
      url: '/api/user/invite',
      method: 'POST'
    });
  }

  return (
    <div>
      <Button action={ openDialog } />
    <div/>
  );
}
```

### Params&#x20;

| Param       | Description                                   | Required | Value        |
| ----------- | --------------------------------------------- | -------- | ------------ |
| children    | children to render a custom dialog            | optional | component(s) |
| description | description message                           | optional | string       |
| form        | a [form object](/gravity-web/components/form) | optional | object       |
| onClose     | callback executed when closed                 | required | function     |
| open        | override the internal open state              | optional | boolean      |
| title       | dialog title                                  | required | string       |
| method      | HTTP post type                                | optional | string       |
| url         | destination to send the form                  | optional | string       |

### Notes

* The `Dialog` component uses `DialogPrimitive` components from `@radix-ui/react-dialog`.
* The `onClose` prop is a callback function that gets executed when the dialog is closed.
* The `open` prop can be used to control the open state of the dialog.
* The `title` and `description` props provide the title and description for the dialog.
* For more details, refer to the [Shadcn Dialog documentation](https://ui.shadcn.com/docs/components/dialog).


# Dropdown

The `Dropdown` component displays a menu to the user, such as a set of actions or functions, triggered by a button.

### Preview

<div align="left"><figure><img src="/files/nZCHsxoWi0zPgaofUswm" alt="Gravity dropdown component" width="186"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <DropdownMenu>
      <DropdownMenuTrigger >Open Menu</DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem>Item 1</DropdownMenuItem>
        <DropdownMenuItem>Item 2</DropdownMenuItem>
        <DropdownMenuItem>Item 3</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}
```

### Props

| Prop      | Description          | Required | Value            |
| --------- | -------------------- | -------- | ---------------- |
| children  | children to render   | required | component(s)     |
| className | custom class         | optional | SCSS or Tailwind |
| inset     | toggle triggle style | optional | boolean          |

### Example

```javascript
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from 'components/lib';

export function User(){

  return (
    <div>
      <DropdownMenu>

        <DropdownMenuTrigger asChild>
          Trigger
        </DropdownMenuTrigger>

        <DropdownMenuContent align='end'>
        
          <DropdownMenuItem>
            <Button 
              variant='naked' 
              icon='user' 
              text='Edit Account' 
              url='/account/profile'
            />
          </DropdownMenuItem>

          <DropdownMenuItem>
            <Button 
              variant='naked' 
              icon='help-circle' 
              text='Help' 
              url='/help'
            />
          </DropdownMenuItem>

        </DropdownMenuContent>
      </DropdownMenu> 
    </div>
  )
}
```

### Notes

* The `Dropdown` component uses `DropdownMenuPrimitive` components from `@radix-ui/react-dropdown-menu`.
* The `children` prop is required and should include the elements to be displayed in the dropdown.
* The `className` prop allows for custom styling.
* The `inset` prop toggles the trigger style.
* For more details, refer to the [Shadcn Dropdown Menu documentation](https://ui.shadcn.com/docs/components/dropdown-menu).


# Feedback

The `Feedback` component is a widget for collecting user feedback. It provides a rating system with optional comments and integrates with the backend API for submitting feedback. Results are available in the [Mission Control app](/mission-control/feedback).

### Preview

<div align="left"><figure><img src="/files/wlloepTrjVvTAbrvrh4S" alt="Gravity feedback component" width="261"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Feedback } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <div>
      <Feedback />
    </div>
  );
}
```

### Notes

* The `Feedback` component uses the `Popover`, `PopoverTrigger`, `PopoverContent`, `Form`, `Button`, `Icon`, and `ViewContext` from `'components/lib'`.
* The `Feedback` component manages its own state for displaying the rating and comments form.
* The `icons` array defines the icons and colors for the different rating options.
* The `saveRating` function sets the selected rating and shows the comments form.
* The `Form` component is used to submit feedback to the backend API.


# Form

You will never experience the pain of coding another HTML form again. The `Form` component is a self-validating form that accepts an object for inputs. It supports various input types, validation, and can handle file uploads and Stripe payments.

### Preview

<div align="left"><figure><img src="/files/JP7ixWuazAzB8xfp2knD" alt="Gravity form component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Form } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <Form 
      inputs={
        name: {
          type: 'text',
          label: 'Name',
          required: true,
        },
        email: {
          type: 'email',
          label: 'Email',
          required: true,
        },
      }
      url='/api/user'
      method='PATCH'
      buttonText='Save'
      callback={(res) => console.log(res)}
    />
  );
}
```

### Props

| Prop           | Description                                  | Required | Value                                                            |
| -------------- | -------------------------------------------- | -------- | ---------------------------------------------------------------- |
| buttonText     | submit button tet                            | optional | string                                                           |
| callback       | function executed on successful submit       | optional | function                                                         |
| cancel         | cancel callback (also shows a cancel button) | optional | function                                                         |
| className      | custom styling                               | optional | SCSS or Tailwind                                                 |
| destructive    | set submit button color to red               | optional | boolean                                                          |
| inputs         | inputs object                                | required | [object](/gravity-web/components/form#inputs-object) (see below) |
| method         | HTTP request type                            | optional | string                                                           |
| onChange       | callback function executed on input change   | optional | function                                                         |
| redirect       | url to redirect to after a successful submit | optional | string                                                           |
| submitOnChange | submit the form on each input change         | optional | boolean                                                          |
| url            | url to post the form to                      | optional | string                                                           |

### Inputs Object

When constructing a form object, each outer key represents an input name and is associated with an object containing various input properties.

```javascript
formData = {
 name: {
  label: 'Your name',
  type: 'text',
  required: true,
  placeholder: 'Jon Smith',
  errorMessage: 'Please enter your name',
 }
}
```

## Input Types

The following input types are available.

* card (creditcard input)
* checkbox
* date
* email
* file
* hidden
* number
* password
* phone
* radio
* select
* switch
* text
* textarea
* url
* otp (one-time password)

## Input Props

Props are passed to the input by the form. A full list of props for the inputs can be found below.

| Prop             | Description                                       | Required | Value                  |
| ---------------- | ------------------------------------------------- | -------- | ---------------------- |
| aria-describedby | id of the element that describes the input        | optional | string                 |
| aria-invalid     | determines if the input is valid                  | required | boolean                |
| className        | custom style                                      | optional | SCSS or Tailwind style |
| defaultValue     | default value                                     | optional | string                 |
| disabled         | disable the input                                 | optional | boolean                |
| id               | html id for the input                             | optional | string                 |
| name             | input name                                        | required | string                 |
| onChange         | callback function executed on change              | required | function               |
| placeholder      | placeholder text                                  | optional | string                 |
| required         | determines if a value is required                 | optional | boolean                |
| value            | current value                                     | optional | string                 |
| min              | minimum value                                     | optional | integer                |
| minLength        | minimum length                                    | optional | integer                |
| max              | maximum value                                     | optional | integer                |
| maxLength        | maximum length                                    | optional | integer                |
| options          | array of options for a radio, checkbox, or select | optional | array                  |

### Form Validation

The form will validate the standard input types, as defined in the form/input/map.js file. Here you can extend the validation and add your own custom validation rules.

```javascript
// default phone validation
phone: {
 component: Input,
  showIcon: true,
  showLabel: true,
  validation: {
    default: /^\+?(?:[0-9] ?){6,14}[0-9]$/
  }
}

// custom validation rule
password: {
  component: Input,
  showIcon: true,
  showLabel: true,
  validation: {
    complex:  /^(?=.*[!@#$%^&*(),.?":{}|<>]).*$/
  }
},

// usage
<Form inputs={{
  password: {
  label: t('auth.signup.account.form.password.label'),
  type: 'password',
  required: true,
  validation: { complex: true }
},
```

### Form Submission

When your form is submitted, Gravity will optimise the request and only send the name/value pairs. On the server, you can access a submitted value using:

```javascript
req.body.email
```

### Form Errors

To show an error on a specific form input, throw an error on the server with an `inputError` key and the input name.

```javascript
throw ({ inputError: 'email', message: `You're already registered` });
```

## Payment Form

To create a Stripe payment form, use the `<PaymentForm>` component. This is a standard form (as above) wrapped in a Stripe provider.

### Example

```javascript
import { Form } from 'components/lib';

function Example({ ...props }){

  return (
    <Form 
      inputs={{
        name: {
         label: 'Your name',
         type: 'text',
         required: true,
         description: 'Your full name',
         placeholder: 'Jon Smith',
         errorMessage: 'Please enter your name',
        },
        email: {
         label: 'Email address',
         type: 'email',
         required: true,
        },
        age: {
         label: 'Age',
         type: 'number',
         min: 18,
         max: 65,
         required: false,
        },
        gender: {
         label: 'Gender',
         type: 'radio',
         options: ['male', 'female'],
         required: true,
        },
        plan: {
         label: 'Billing Plan',
         type: 'select',
         options: [
          { value: 'plan_startup', label: 'Startup' },
          { value: 'plan_enterprise', label: 'Enterprise' }
         ],
         defaultValue: 'plan_startup',
         required: true,
       } 
      }
      url='/api/user'
      method='PATCH'
      buttonText='Submit'
      callback={(res) => console.log(res)}
    />
  );
}
```

### Notes

* The `Form` component uses the `useForm` and `Controller` from `react-hook-form` for form management and validation.
* The `inputs` prop defines the form inputs and their configurations.
* The `buttonText` prop specifies the text for the submit button.
* The `callback` prop is a function executed on successful form submission.
* The `destructive` prop sets the submit button color to red.
* The `submitOnChange` prop submits the form on each change.
* The `url` and `method` props define the endpoint and HTTP method for form submission.
* The `Form` component also includes support for Stripe payments through the `PaymentForm` component.
* For more details, refer to the [Shadcn Form documentation](https://ui.shadcn.com/docs/components/form).


# Grid

The `Grid` component is a responsive grid layout that supports up to six columns. It allows for custom styling and flexible child component rendering.

### Preview

<div align="left"><figure><img src="/files/PBA4ybmJQTLQiMtQ1T7D" alt="Gravity grid component"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Grid } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <Grid max={ 4 }>
      <div>Item 1</div>
      <div>Item 2</div>
      <div>Item 3</div>
      <div>Item 4</div>
    </Grid>
  );
}

```

### Props

| Prop      | Description               | Required | Value                    |
| --------- | ------------------------- | -------- | ------------------------ |
| children  | the children to render    | required | component(s)             |
| className | custom styles             | optional | SCSS or Tailwind style   |
| max       | maximum number of columns | required | integer (2-8) default: 2 |

### Notes

* The `children` prop allows for flexible rendering of child components within the grid.
* The `max` prop specifies the maximum number of columns the grid can have


# Header

The `Header` component provides a header section with a title and optional children components.

### Preview

<div align="left"><figure><img src="/files/J9vlsNN0wdfgaA5Nzilr" alt="Gravity header component" width="563"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Header } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <Header title='My View Title'>
      <p>Optional content goes here.</p>
    </Header>
  );
}
```

### Props

| Prop     | Description        | Required | Value        |
| -------- | ------------------ | -------- | ------------ |
| children | children to render | optional | component(s) |
| title    | title of the view  | required | string       |

### Notes

* The `title` prop provides the title for the header section.
* The `children` prop allows for rendering additional components within the header.


# Helper

{% hint style="danger" %}
Depreciated. Use [Alert](/gravity-web/components/alert).
{% endhint %}

The `<Helper/>` component is a simple way to highlight additional information to the user with text and/or a link.

## Preview

<div align="left"><figure><img src="/files/tc7PR4Ka3Ob1r5Y87ETK" alt="" width="336"><figcaption></figcaption></figure></div>

## Code

```javascript
<Helper 
 text='Need help? Refer to the database docs' 
 url='https://docs.usegravity.app/gravity-server/installation/database-setup'
/>
```

## Props

| Prop | Description              | Required | Value  |
| ---- | ------------------------ | -------- | ------ |
| text | text label               | required | string |
| url  | link to more information | optional | string |


# Icon

The `Icon` component renders an icon from [Lucide React](https://lucide.dev/guide/packages/lucide-react). Icons are lazy-loaded to prevent increasing bundle size.

### Preview

<div align="left"><figure><img src="/files/j9wsF5UlOusYeDsvZgv6" alt="Gravity icon component"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Icon } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <div>
      <Icon name='alert-circle' size={ 16 } color='red' />
      <Icon name='check' size={ 24 } color='#10b981' />
    </div>
  );
}

```

### Props

| Prop      | Description        | Required | Value                                                   |
| --------- | ------------------ | -------- | ------------------------------------------------------- |
| className | custom styles      | optional | SCSS or Tailwind                                        |
| color     | icon outline color | optional | string (light/dark/green/blue/orange/red) default: dark |
| name      | icon name          | required | string (see [Lucide Icons](https://lucide.dev/icons/))  |
| size      | icon size          | required | integer, default: 16                                    |

### Notes

* The `Icon` component uses `lazy`, `Suspense`, and `useMemo` from React to handle lazy loading of icons.
* The `name` prop specifies the icon image to use and converts it to PascalCase if necessary.
* The `color` and `fill` props allow for customizing the outline and fill colors of the icon.
* Icons are loaded from the `lucide-react` library.


# Image

The `Image` component is an image wrapper that requires the image to be imported before passing it to the `src` prop.

```javascript
import Logo from './images/logo.svg';

<Image
  src={ Logo }
  title='Gravity Logo'
  alt='Awsome logo'
/>
```

### Props

| Prop      | Description     | Required | value            |
| --------- | --------------- | -------- | ---------------- |
| alt       | alt description | required | string           |
| className | custom style    | optional | SCSS or Tailwind |
| src       | imported source | required | image            |
| title     | description     | required | string           |

### Notes

* The `src` prop should be an imported image source.
* The `alt` and `title` props provide alternative text and a description for the image, respectively.


# Layout

Gravity Web offers four versatile layout options. Layouts contain the common components to be rendered across all views that utilise that layout, for example: **navigation** and **header.**

### Preview

#### Account Layout

<figure><img src="/files/yIxKQjNzonU9eOwdAMCb" alt="Gravity account layout component"><figcaption></figcaption></figure>

#### App Layout

<figure><img src="/files/zDAx0c3hUHOP6Cye0mx4" alt="Gravity app layout component"><figcaption></figcaption></figure>

#### Auth Layout

<figure><img src="/files/8N5fdi0cwrXgoUAtCRXJ" alt="Gravity auth layout component"><figcaption></figcaption></figure>

#### Onboarding Layout

<figure><img src="/files/ukWa2yATRDPjVEYw5yqf" alt="Gravity onboarding layout component"><figcaption></figcaption></figure>

### Props

| Prop     | Description        | Required | Value        |
| -------- | ------------------ | -------- | ------------ |
| children | children to render | required | component(s) |
| title    | view title         | required | string       |

### Usage

You can use a layout by passing its name in the [route object](/gravity-web/routing).

```javascript
{
    path: '/dashboard',
    view: Dashboard,
    layout: 'app',
    title: 'account.index.title'
}
```


# Link

The `Link` component routes a new view within the application router. It should be used instead of `<a>` to avoid reloading the page.

### Usage

```javascript
import { Link } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <div>
      <Link url='/' title='Home' text='Go to Home' />
    </div>
  );
}
```

### Props

<table data-full-width="true"><thead><tr><th>Prop</th><th>Description</th><th>Required</th><th>Value</th></tr></thead><tbody><tr><td>children</td><td>children to render</td><td>required if no text prop</td><td>component(s)</td></tr><tr><td>className</td><td>custom style</td><td>optional</td><td>SCSS or Tailwind</td></tr><tr><td>color</td><td>link color</td><td>optional</td><td>string (dark/light), default: primary</td></tr><tr><td>text</td><td>link text </td><td>required</td><td>string</td></tr><tr><td>title</td><td>link title </td><td>required</td><td>string</td></tr><tr><td>url</td><td>destination url</td><td>required</td><td>string</td></tr></tbody></table>

### Notes

* The `Link` component uses the `NavLink` from `react-router-dom` for internal routing and `<a>` for external links.
* The `className` prop allows for custom styling to be applied.
* The `color` prop sets the link color and defaults to the primary color if not specified.
* The `text` and `title` props provide the link text and title, respectively.
* The `url` prop specifies the destination URL.


# List

The `List` component renders an ordered or unordered list based with the provided items.

### Ordered & Unordered

```javascript
import { List } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <div>
      <List items={['Item 1', 'Item 2', 'Item 3']} ordered/>
    </div>
  );
}

```

### Props

| Props     | Description          | Required | Value            |
| --------- | -------------------- | -------- | ---------------- |
| className | custom style         | optional | SCSS or Tailwind |
| items     | list of items        | required | array of strings |
| ordered   | show an ordered list | optional | boolean          |

### Notes

* The `List` component uses the `cn` function from `'components/lib'` for class name handling.
* The `className` prop allows for custom styling to be applied.
* The `items` prop specifies the list of items to be displayed.
* The `ordered` prop determines whether the list is ordered (`<ol>`) or unordered (`<ul>`).


# Loader

The `Loader` component provides an infinite spinning animation for indicating loading states.

### Preview

<div align="left"><figure><img src="/files/ZLZabs6nyjx4Biuooe1v" alt="Gravity loader component" width="25"><figcaption></figcaption></figure></div>

## Usage

```javascript
import { Loader } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <div>
      <Loader />
    </div>
  );
}
```

### Props

| Prop      | Description  | Required | Value            |
| --------- | ------------ | -------- | ---------------- |
| className | custom style | optional | SCSS or Tailwind |

### Notes

* The `Loader` component uses the `Icon` component from `'components/lib'` to display a loading icon.
* The `className` prop allows for custom styling to be applied.


# Logo

Display your logo with pride. The `Logo` component renders a logo image. It allows toggling between the brand color or a white logo and can display either the full logo or  the logo mark.&#x20;

You should replace the SVG files in the component folder with your own logo images.

### Usage

```javascript
import { Logo } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <div>
      <Logo/>
    </div>
  );
}

```

## Props

| Prop      | Description                              | Required | Value                   |
| --------- | ---------------------------------------- | -------- | ----------------------- |
| className | custom style                             | optional | SCSS or Tailwind        |
| color     | toggle between brand color or white logo | optional | string, default: white  |
| mark      | use a logo mark instead of the full logo | optional | boolean, default: false |

### Notes

* The `Logo` component uses the `AuthContext` to check for dark mode and force the white logo if dark mode is enabled.
* The `color` prop toggles between the brand color and the white logo.
* The `mark` prop determines whether to display the logo mark or the full logo.
* The `className` prop allows for custom styling to be applied.


# Nav

Gravity provides four navigation menu types that work across mobile and desktop devices.

## Vertical Nav

The `VerticalNav` component is the primary desktop navigation used inside the main app. It displays navigation items with optional tooltips and is designed for large screen sizes.&#x20;

### Preview

<div align="left"><figure><img src="/files/cEMy9BNQbkalqzSezk3G" alt="Gravity vertical nav component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { VerticalNav } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <VerticalNav
      items={[
        { label: 'Dashboard', icon: 'activity', link: '/dashboard', position: 'top' },
        { label: 'Account', icon: 'user', link: '/account', position: 'top' },
        { label: 'Sign Out', icon: 'log-out', action: signout, position: 'bottom' }
      ]}
    />
  )
}
```

### Props

| Prop  | Description               | Required | Value                                                                              |
| ----- | ------------------------- | -------- | ---------------------------------------------------------------------------------- |
| items | array of navigation items | required | array of objects ({ label: string, link: string, icon: string, position: string }) |

### Notes

* The `VerticalNav` component uses the `NavLink` from `react-router-dom` for navigation links.
* The `Logo`, `Button`, `Icon`, `Tooltip`, `TooltipTrigger`, and `TooltipContent` components from `'components/lib'` are used for rendering the navigation items with tooltips.
* The `items` prop specifies the navigation items, each containing a `label`, `link`, `icon`, and `position`.
* The `position` property in each item determines whether the item is placed at the top or bottom of the navigation.

***

## Drawer Nav

The `DrawerNav` component is the primary mobile navigation used inside the main app. It displays navigation items within a drawer that can be opened and closed.

### Preview

<div align="left"><figure><img src="/files/kNatNzg7j1UaOEArpbHh" alt="Gravity drawer nav component" width="370"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { VerticalNav } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <DrawerNav
      items={[
        { label: 'Dashboard', icon: 'activity', link: '/dashboard' },
        { label: 'Account', icon: 'user', link: '/account' },
        { label: 'Sign Out', icon: 'log-out', action: signout }
      ]}
    />
  )
}
```

### Props

| Prop  | Description               | Required | Value                                                                              |
| ----- | ------------------------- | -------- | ---------------------------------------------------------------------------------- |
| items | array of navigation items | required | array of objects ({ label: string, link: string, icon: string, position: string }) |

### Notes

* The `DrawerNav` component uses the `Sheet`, `SheetClose`, `Button`, `Icon`, `Logo`, and `NavLink` components from `'components/lib'`.
* The `items` prop specifies the navigation items, each containing a `label`, `link`, `icon`, and `position`.
* The `Sheet` component provides the drawer functionality.
* For more details, refer to the [Shadcn Sheet documentation.](https://ui.shadcn.com/docs/components/sheet)

***

## Sub Nav

The `SubNav` component is a sub-navigation element that displays a list of navigation items. It supports permission-based item visibility and is fully responsive; displaying a select input on mobile, a horizontal bar on medium sized screens and a vertical sidebar on larger screens.&#x20;

### Preview

<div align="left"><figure><img src="/files/zkUKiVtNaWkFsr365vlr" alt="Gravity sub nav component" width="563"><figcaption></figcaption></figure></div>

### Usage

````javascript
import { SubNav } from 'components/lib';

function MyComponent({ ...props }){

  const subnav = [
    { label: 'Profile', link: '/account/profile', icon: 'user', permission: 'user' },
    { label: 'Password', link: '/account/password', icon: 'lock', permission: 'user' },
    { label: 'Billing', link: '/account/billing', icon: 'credit-card', permission: 'owner' },
    { label: 'API Keys', link: '/account/apikeys', icon: 'key', permission: 'developer' },
    { label: 'Users', link: '/account/users', icon: 'users', permission: 'admin' }
  ]

```

  return (
    <div>
      <SubNav items={ items } />
    </div>
  );
}

````

### Props

| Prop        | Description               | Required | Value                                                                                 |
| ----------- | ------------------------- | -------- | ------------------------------------------------------------------------------------- |
| transparent | array of navigation items | optional | array of objects ({ label: string, link: string, icon: string, permission?: string }) |

### Notes

* The `SubNav` component uses `NavLink` from `react-router-dom` for navigation links.
* The `AuthContext` is used to handle permission-based item visibility.
* The `Select` component allows for navigation via a dropdown on smaller screens.
* The `Icon` component is used to display icons for each navigation item.
* The `location` and `navigate` hooks from `react-router-dom` are used for managing navigation.


# Onboarding

A great way to increase engagement and retention is to provide an onboarding flow that guides users through completing the key actions needed to get maximum benefit from your application.

The `Onboarding` component is a flow to help users set up the app. It accepts multiple views and marks the user as onboarded when the process is completed if the `save` prop is true.

### Preview

<div align="left"><figure><img src="/files/EDJKK8ji7vpHdjE8OzEh" alt="Gravity onboarding component" width="563"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Onboarding } from 'components/lib';

function MyComponent({ ...props }){

  const views = [
    { 
      name: 'Welcome', 
      description: 'Introduction to the app', 
      component: <Welcome /> 
    },
    { 
      name: 'Profile Setup', 
      description: 'Set up your profile', 
      component: <ProfileSetup /> 
    },
    { 
      name: 'Preferences', 
      description: 'Set your preferences', 
      component: <Preferences /> 
    },
  ];

  return (
    <Onboarding views={ views } onFinish='/dashboard' save={ true } />
  );
}
```

### Props

| Prop     | Description                                                 | Required | Value                                                                          |
| -------- | ----------------------------------------------------------- | -------- | ------------------------------------------------------------------------------ |
| onFinish | url to navigate to when finished                            | required | string, default: /dashboard                                                    |
| save     | Set onboarded column in user database to true on completion | optional | boolean                                                                        |
| views    | array of child views                                        | required | array of objects ({ name: string, description: string, component: component }) |

### Notes

* The `Onboarding` component uses `CheckList`, `Button`, `Logo`, `useNavigate`, `Event`, `Pagination`, `useLocation`, and `useTranslation` from `'components/lib'`.
* The `views` prop specifies the list of views for the onboarding process, each containing a `name`, `description`, and `component`.
* The `onFinish` prop specifies the URL to navigate to when the onboarding process is finished.
* The `save` prop indicates whether to set the `onboarded` column in the user database to true upon completion.
* The `PaginationNav` function handles the pagination navigation between views.


# Pagination

The `Pagination` component provides pagination with page navigation, next and previous links. The pagination is generated dynamically using query params: ?page=1

### Preview

<div align="left"><figure><img src="/files/vK7IPvsDkDjg87Pwa5AE" alt="Gravity pagination component" width="184"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Pagination } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <div>
      <Pagination total={ 5 } limit={ 25 } className={ Style.pagination }/>
    </div>
  );
}

```

### Props

| Prop      | Description                                                                                                                | Required | Value            |
| --------- | -------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------- |
| children  | child components as per [Shadcn docs](https://ui.shadcn.com/docs/components/pagination) if not using total and limit props | optional | component(s)     |
| className | custom styles                                                                                                              | optional | SCSS or Tailwind |
| total     | the total number of items in the list                                                                                      | required | integer          |
| limit     | the number of items per page                                                                                               | required | integer          |

### Notes

* The `Pagination` component uses various sub-components like `PaginationContent`, `PaginationItem`, `PaginationLink`, `PaginationPrevious`, `PaginationNext`, and `PaginationEllipsis`.
* The `total` prop specifies the total number of items in the list
* The `limit` prop determines how many items to show per page
* The `className` prop allows for custom styling to be applied.
* If `items` is not provided, the `children` prop can be used to pass custom child components.
* For more details, refer to the [Shadcn Pagination documentation](https://ui.shadcn.com/docs/components/pagination).


# Popover

The `Popover` component displays rich content in a portal, triggered by a button.

### Preview

<div align="left"><figure><img src="/files/uRgAst8vTAXUXCaDiu3u" alt="Gravity popover component" width="272"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Popover, PopoverTrigger, PopoverContent } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <div>
      <Popover>
      
        <PopoverTrigger asChild>
          <button>Open Popover</button>
        </PopoverTrigger>
        
        <PopoverContent align='center' sideOffset={ 4 }>
          <div>Content goes here.</div>
        </PopoverContent>
        
      </Popover>
    </div>
  );
}

```

### Props

| Prop       | Description                               | Required | Value                                      |
| ---------- | ----------------------------------------- | -------- | ------------------------------------------ |
| align      | alignment of the content                  | optional | string (start/center/end), default: center |
| children   | `Trigger` and `PopoverContent` components | required | components                                 |
| className  | custom styles                             | optional | SCSS or Tailwind                           |
| sideOffset | offset for the popover content            | optional | integer, default: 4                        |

### Notes

* The `Popover` component uses `PopoverPrimitive` from `@radix-ui/react-popover` for the popover functionality.
* The `align` prop specifies the alignment of the popover content and defaults to 'center'.
* The `sideOffset` prop sets the offset for the popover content and defaults to 4.
* The `className` prop allows for custom styling to be applied.
* The `children` prop should include the `PopoverTrigger` and `PopoverContent` components.
* For more details, refer to the [Shadcn Popover documentation](https://ui.shadcn.com/docs/components/popover).


# Progress

### Progress Bar

The `Progress` component displays a bar indicator to represent progress as a percentage.

### Preview

<div align="left"><figure><img src="/files/2LhlsJFWBnzoqkMpMnV0" alt="Gravity progress indicator component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Progress } from 'components/lib';

function Example({ ...props }){
  return (
    <div>
      <Progress value={ 50 } />
    </div>
  );
}
```

### Props

| Prop      | Description      | Required | Value            |
| --------- | ---------------- | -------- | ---------------- |
| className | custom styling   | optional | SCSS or Tailwind |
| value     | percentage value | required | integer          |

### Notes

* The `Progress` component uses `ProgressPrimitive` from `@radix-ui/react-progress` for the progress functionality.
* The `value` prop specifies the percentage value to be displayed by the progress bar.
* The `className` prop allows for custom styling to be applied.
* The progress indicator's transform is calculated based on the `value` prop to visually represent the progress.
* For more details, refer to the [Shadcn Progress documentation](https://ui.shadcn.com/docs/components/progress).


# Row

The `Row` component adds space below a UI element or group of UI elements. It can optionally restrict the width of the row.

```javascript
import { Row } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <div>
      <Row width='md'>
        <div>Element 1</div>
        <div>Element 2</div>
      </Row>
    </div>
  );
}
```

### Props

| Prop      | Description                   | Required | Value                |
| --------- | ----------------------------- | -------- | -------------------- |
| children  | children to render            | required | component            |
| className | custom style                  | optional | SCSS or Tailwind     |
| mainTitle | the main title of the row     | optional | string               |
| width     | restrict the width of the row | optiona  | string (sm/md/lg/xl) |

### Notes

* The `children` prop allows for rendering nested components within the row.
* The `width` prop restricts the width of the row if provided.
* The `className` prop allows for custom styling to be applied.


# Search

The `Search` component is a search input field that executes a callback on change and submit. The callback can be throttled to control its execution frequency.

## Preview

<div align="left"><figure><img src="/files/p4NJNALldFE94obMSaPd" alt="Gravity search component" width="375"><figcaption></figcaption></figure></div>

## Usage

```javascript
import { Search } from 'components/lib';

function MyComponent({...props }){

  const handleSearch = (value) => {
    console.log('Search value:', value);
  };

  return (
    <div>
      <Search callback={ handleSearch } throttle={ 300 } placeholder='Search users...' />
    </div>
  );
}
```

### Props

| Prop        | Description                                    | Required | Value                    |
| ----------- | ---------------------------------------------- | -------- | ------------------------ |
| callback    | executed on change and submit                  | required | function                 |
| className   | custom style                                   | optional | SCSS or Tailwind         |
| placeholder | placeholder text                               | optional | string (default: Search) |
| throttle    | throttle the callback function execution in ms | optional | integer                  |

### Notes

* The `Search` component uses the `Input` component from `'components/lib'` for the search input field.
* The `callback` prop specifies the function to be executed on change and submit.
* The `throttle` prop controls the frequency of callback execution in milliseconds.
* The `placeholder` prop provides the placeholder text for the search input and defaults to 'Search'.
* The `value` prop represents the current value of the search input.
* The `className` prop allows for custom styling to be applied.
* The `debounce` function is used to throttle the callback execution.


# Separator

The `Separator` component provides a visual divider to separate content in a layout. It supports both horizontal and vertical orientations and can include an optional label.

### Preview

<div align="left"><figure><img src="/files/nK4WGxCrXFo8rV1UdqUI" alt="" width="254"><figcaption></figcaption></figure></div>

### Usage

```
import { Separator } from 'components/lib';

function Example(){
  return (
    <div>
    
      <Separator label='Section 1' orientation='horizontal' />
      <div>Content for section 1</div>
      <Separator label='Section 2' orientation='horizontal' />
      <div>Content for section 2</div>
      
    </div>
  );
}
```

### Props

| Prop        | Description                                | Required | Value                                             |
| ----------- | ------------------------------------------ | -------- | ------------------------------------------------- |
| className   | custom style                               | optional | SCSS or Tailwind                                  |
| label       | optional label to display                  | optional | string                                            |
| orientation | orientation of the separator               | optional | string (horizontal/vertical), default: horizontal |
| decorative  | whether the separator is purely decorative | optional | boolean, default: true                            |

### Notes

* The `Separator` component uses the `@radix-ui/react-separator` library for its core functionality.
* The `orientation` prop can be set to "horizontal" or "vertical" to control the direction of the separator.
* The `decorative` prop is set to `true` by default, meaning the separator is purely visual and doesn't convey any semantic meaning. If `decorative` is set to `false`, it will convey a meaning to assistive technologies.
* You can apply custom styles through the `className` prop. The component uses predefined styles from the `separator.tailwind.js` file.
* If a `label` prop is provided, it will be displayed within the separator, useful for denoting sections.
* For more information refer to the [Separator Shadcn documentation](https://ui.shadcn.com/docs/components/separator).


# Sheet

The `Sheet` component overlays a modal on the top, left, bottom, or right of the viewport.

### Preview

<div align="left"><figure><img src="/files/kNatNzg7j1UaOEArpbHh" alt="Gravity sheet component" width="370"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Sheet } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <div>
      <Sheet 
        trigger={ <button>Open Sheet</button> }
        title="Sheet Title"
        description="This is a description of the sheet."
        side="right">
        
          <div>Sheet content goes here.</div>
        
      </Sheet>
    </div>
  );
}
```

### Props

| Prop      | Description                  | Required | Value                                          |
| --------- | ---------------------------- | -------- | ---------------------------------------------- |
| children  | child component(s)           | required | component(s)                                   |
| className | custom style                 | optional | SCSS or Tailwind                               |
| side      | side where the sheet appears | optional | string (left/right/top/bottom), default: right |
| title     | sheet title                  | optional | string                                         |
| trigger   | trigger component            | required |                                                |

### Notes

* The `Sheet` component uses `SheetPrimitive` from `@radix-ui/react-dialog` for the sheet functionality.
* The `side` prop specifies the side of the viewport where the sheet will appear and defaults to 'right'.
* The `className` prop allows for custom styling to be applied.
* The `trigger` prop specifies the component that triggers the opening of the sheet.
* The `title` and `description` props provide additional context for the sheet.
* The `children` prop allows for rendering nested components within the sheet.
* For more details, refer to the [Shadcn Sheet documentation](https://ui.shadcn.com/docs/components/sheet).


# Social

## Social Share

The `SocialShare` component is a sharing widget for Facebook, Twitter, LinkedIn, and email. It provides buttons to share a URL with a description on these social platforms.

### Preview

<div align="left"><figure><img src="/files/3AfrPnrduIj4umPeA9mc" alt="Gravity social share component" width="237"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { SocialShare } from 'components/lib';

function MyComponent({ ...props }){

  const url = 'https://example.com';
  const description = 'Check out this amazing website!';

  return (
    <div>
      <SocialShare url={ url } description={ description } />
    </div>
  );
}

```

### Props

| Prop        | Description                    |          | value                  |
| ----------- | ------------------------------ | -------- | ---------------------- |
| className   | custom styles                  | optional | SCSS or Tailwind style |
| description | text for the social media post | required | string                 |
| url         | url of the page to share       | required | string                 |

### Notes

* The `SocialShare` component uses the `Button` component from `'components/lib'` to create sharing buttons.
* The `networks` object defines the sharing URLs for Facebook, Twitter, LinkedIn, and email.
* The `className` prop allows for custom styling to be applied.
* The `description` prop provides the text for the social media post.
* The `url` prop specifies the URL of the page to share.

***

## Social Sign-In

Gravity supports [social sign-](/gravity-server/authentication/social-sign-on)[ons](/gravity-server/authentication/social-sign-on) with over 500+ networks.&#x20;

The `SocialSignin` component provides buttons for signing up or signing in with social networks such as Facebook, Google, Twitter, and 500+ other networks supported by Passport.js.

### Preview

<div align="left"><figure><img src="/files/K4vFM5qZRCUpRjbdmNk0" alt="Gravity social sign-on component" width="375"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { SocialSignin } from 'components/lib';

function MyComponent({ ...props }){

  const networks = ['facebook', 'twitter'];

  return (
    <div>
      <SocialSignin network={ networks } />
    </div>
  );
}

```

### Props

| Prop      | Description                      | Required | Value            |
| --------- | -------------------------------- | -------- | ---------------- |
| className | custom style                     | optional | SCSS or Tailwind |
| invite    | user is being invited as a child | optional | boolean          |
| network   | array of social network names    | required | array            |
| signup    | user is signing up a new account | optional | boolean          |

### Notes

* The `SocialSignin` component uses the `Button`, `cn`, `Grid`, and `useTranslation` components from `'components/lib'`.
* The `network` prop specifies the list of social networks for sign-in/sign-up, each represented as a string.
* The `invite` and `signup` props control whether the user is invited as a child or signing up, respectively.
* The `className` prop allows for custom styling to be applied.
* The `loading` state is managed for each network button to indicate loading status.
* The `serverURL` is constructed based on the current environment settings.


# Stat

The `Stat` component displays a statistic value with an optional icon and a positive or negative change value.

### Preview

<div align="left"><figure><img src="/files/yvNsNzwJGMKhwGxwSrDg" alt="Gravity stat component" width="292"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Stat } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <div>
      <Stat 
        value="1200" 
        label="Total Sales" 
        change='+10%'
        icon="dollar-sign" 
      />
    </div>
  );
}
```

## Props

| Prop      | Description                                      | Required | Value             |
| --------- | ------------------------------------------------ | -------- | ----------------- |
| change    | positive/negative value indicating change amount | optional | string            |
| className | custom style                                     | required | SCSS or Tailwind  |
| icon      | [icon](/gravity-web/components/icon) name to use | optional | string            |
| label     | value label                                      | required | string            |
| value     | value                                            | required | integer or string |

### Notes

* The `Stat` component uses the `Icon` and `cn` functions from `'components/lib'` for handling icons and class names, respectively.
* The `change` prop specifies the change value, which can be positive or negative.
* The `className` prop allows for custom styling to be applied.
* The `icon` prop specifies the name of the icon to be displayed.
* The `label` prop is required and provides a description for the value.
* The `value` prop is required and represents the numeric or string value to be displayed.
* The `changeUp` variable is used to determine if the change value is positive or negative.


# Table

Are you familiar with the pain of marking up HTML tables? I don't think I'll ever forget it. Fortunately, you'll never have to mark up another table again.

The `Table` component provides a dynamic table with sorting, search, and actions. Header rows are created dynamically from column names unless specified.

### Preview

<div align="left"><figure><img src="/files/yvc64nA1bxRS1IyPqej1" alt="Gravity table component" width="563"><figcaption></figcaption></figure></div>

## Code

```javascript
import { Table } from 'components/lib';

function MyComponent({ ...props }){

  const data = [
    { id: 1, email: 'user1@example.com', date_created: '2023-01-01' },
    { id: 2, email: 'user2@example.com', date_created: '2023-01-02' },
  ];

  return (
    <div>
      <Table
        data={ data }
        searchable
        selectable
        show=['email', 'date_created']
      />
    </div>
  );
}
```

### Props

<table data-full-width="true"><thead><tr><th>Prop</th><th>Description</th><th>Required</th><th>Value</th></tr></thead><tbody><tr><td>actions</td><td>array of action objects</td><td>optional</td><td><a href="#table-actions">object</a> (see below)</td></tr><tr><td>badge</td><td>add a badge to every row column</td><td>optional</td><td><a href="/pages/-M-BbfdiMbqwn6S44jLw#table-badges">object</a> (see below)</td></tr><tr><td>data</td><td>array of table rows</td><td>required</td><td><a href="#table-data-object">object</a> (see below)</td></tr><tr><td>footer</td><td>footer row</td><td>optional</td><td>object ({ span: integer, value: string })</td></tr><tr><td>header</td><td>array of header column names</td><td>optional</td><td><a href="/pages/-M-63u4w72MmEGDqorml">array</a> (see below)</td></tr><tr><td>hide</td><td>columns names to hide</td><td>optional</td><td>array of strings</td></tr><tr><td>loading</td><td>toggle loading spinner</td><td>optional</td><td>boolean</td></tr><tr><td>searchable</td><td>enable searching the table data</td><td>optional</td><td>boolean</td></tr><tr><td>selectable</td><td>enable selecting table rows</td><td>optional</td><td>boolean</td></tr><tr><td>show</td><td>columns names to show</td><td>optional</td><td>array of strings</td></tr><tr><td>translation</td><td>reference to <a href="/pages/ClBQaXPRz0iLjycVsGXW">locale</a> object for header translations</td><td>optional</td><td>string</td></tr></tbody></table>

### Table Header Object

Pass a header array to the table if you'd like to customise the column headings. If no header is specified, Gravity will use the object key names from the first row and format them.

```javascript
const header = ['Name', 'Email']
```

### Table Data Object

The table body contains an array of objects - each array item generates a new row, and each key/value pair will be mapped to a column.

```javascript
const data = [
  { id: 1, email: 'user1@example.com', date_created: '2023-01-01' },
  { id: 2, email: 'user2@example.com', date_created: '2023-01-02' },
];
```

### Table Links

You can add a link to a table cell by passing an object with { `label`, `url` } keys.

```javascript
const data = [
  { id: 1, email: { label: 'user@example.com', url: '/users/1' }
];
```

### Table Actions

The `actions` prop enables you to create a dropdown menu with action buttons for each table row. \
\
Global actions are also available by passing the `global` key with the optional `globalOnly` key to make this action available only in the global actions at the top of the table. Global actions can be performed on multiple rows when the `selectable` prop is passed to the table.

Global actions also accept a `color` key to set the button color.

Executing an action button will return the data of the row it belongs to so you can access IDs and values.

```javascript
<Table
  data={ data }
  actions={{ 
  
    { icon: 'edit', label: 'Edit', action: editUser }, 
    { icon: 'trash', label: 'Delete, action: deleteUser }, 
    { icon: 'mail', label: 'Contact', action: contactUser },
    { icon: 'circle-plus', 'New', action: createUser, global: true, globalOnly: true, color: 'green' }
  
  }}
/>
```

### Conditional Actions

You can conditionally render an action based on a cells value:

```javascript
{ icon: 'edit', label: 'Edit', action: editUser, condition: { status: 'verified' }
```

### Action Callbacks

Every action will return two callback helper functions: `editRowCallback` and `deleteRowCallback` to help with updating the table state once a row has been edited or deleted.

```javascript
// edit row callback
const editUser = useCallback(({ row, editRowCallback }) => {

  viewContext.dialog.open({
    form: {
      inputs: {
        id: {
          type: 'hidden',
          value: row.id
        },
        email: {
          type: 'email', 
          value: row.email,
          required: true    
        },
        buttonText: 'Edit User',
        url: '/api/user',
        method: 'PATCH'
      }
    }, (form) => {

      const newState = editRowCallback(form);
      setUsers(newState);

  });
}, [viewContext])

// delete row callback
const deleteUser = useCallback(({ row, deleteRowCallback }) => {

  viewContext.dialog.open({
    form: {
      inputs: false,
      buttonText: 'Delete User',
      url: `/api/user/${row.id}`,
      method: 'DELETE',
      destructive: true
    },
  }, () => {

    const newState = deleteRowCallback(row);
    setUsers(newState);

  });
}, [viewContext]);
```

## Table Badges

You can render a colored badge in your table rows using the `badge` pro&#x70;**.** If you need to use conditional colors, pass an array of conditions and the table cell will test them. If you need multiple badges, you can pass an array of these objects.

```javascript
badge={{ col: 'status', color: 'blue', condition: [

  { value: 'registered', color: 'green' },
  { value: 'invited', color: 'blue' }

]}}
```

### Notes

* The `Table` component uses the `Loader`, `Search`, `Icon`, and `cn` functions from `'components/lib'`.
* The `actions` prop specifies the actions that can be performed on the table rows.
* The `badge` prop allows for adding badges to specific columns based on conditions.
* The `data` prop provides the table rows.
* The `footer` prop specifies the footer row with a span and value.
* The `header` prop specifies the header column names.
* The `hide` prop allows for hiding specific columns.
* The `loading` prop toggles the loading spinner.
* The `searchable` prop enables the search field.
* The `selectable` prop allows users to select table rows and perform global actions.
* The `show` prop specifies the columns to show, defaulting to all.
* The `translation` prop refers to a locale object for header translations.
* For more details, refer to the [Shadcn Table documentation](https://ui.shadcn.com/docs/components/table).


# Tabs

The `Tabs` component provides a set of layered sections displayed one at a time, allowing for organized content presentation.

### Preview

<div align="left"><figure><img src="/files/m7WfymGwECCu64TMis6N" alt="Gravity tabs component" width="563"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Tabs, TabsList, TabsTrigger, TabsContent } from 'components/lib';

function MyComponent({ ...props }){

  return (
    <Tabs defaultValue='tab1'>
    
      <TabsList>
        <TabsTrigger value='tab1'>Tab 1</TabsTrigger>
        <TabsTrigger value="tab2">Tab 2</TabsTrigger>
        <TabsTrigger value="tab3">Tab 3</TabsTrigger>
      </TabsList>
      
      <TabsContent value='tab1'>
        <p>Content for Tab 1</p>
      </TabsContent>
      
      <TabsContent value='tab2'>
        <p>Content for Tab 2</p>
      </TabsContent>
      
      <TabsContent value='tab3'>
        <p>Content for Tab 3</p>
      </TabsContent>
      
    </Tabs>
  );
}
```

### Props

| Prop         | Description               | Required | Value            |
| ------------ | ------------------------- | -------- | ---------------- |
| className    | custom style              | optional | SCSS or Tailwind |
| defaultValue | default selected tab name | required | string           |

### Notes

* The `Tabs` component uses `TabsPrimitive` from `@radix-ui/react-tabs` for tab functionality.
* The `className` prop allows for custom styling to be applied.
* The `defaultValue` prop specifies the default selected tab.
* For more details, refer to the [Shadcn Tabs documentation](https://ui.shadcn.com/docs/components/tabs).


# Toast (Notification)

{% hint style="info" %}
Renamed from Notification in Gravity 12.
{% endhint %}

The `Toaster` component provides a notification system using toast messages. It uses a provider to manage and display toast notifications with optional actions and icons.

You can show a notification from anywhere in your application using the [**ViewContext**](/gravity-native/components/view)**.**

### Preview

<div align="left"><figure><img src="/files/NNaM8AWfvL2DY4OVNev0" alt="Gravity toast notification" width="563"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { useContext } from 'react';
import { ViewContext, Button } from 'components/lib';

function MyComponent({ ...props }){

  const viewContext = useContext(ViewContext);
  
  return (
    <div>
      <Button 
        text='Show'
        action={ () => {
          viewContext.notification({ 
            title: 'Hello!', 
            description: 'I am a notification.' 
          });
        }}
      }/>
    </div>
  );
}
```

## Parameters

| Param       | Description       | Required | Value                       |
| ----------- | ----------------- | -------- | --------------------------- |
| description | toast description | optional | string                      |
| title       | toast title       | optional | string                      |
| variant     | variant           | optional | string (success/info/error) |

### Notes

* The `Toaster` component uses the `useToast` hook to manage the state of the toasts.
* The component relies on `Toast`, `ToastClose`, `ToastDescription`, `ToastProvider`, `ToastTitle`, and `ToastViewport` from `./toast.jsx`.
* For more details, refer to the [Shadcn Toast documentation](https://ui.shadcn.com/docs/components/toast).


# Tooltip

The `Tooltip` component provides a popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.

### Preview

<div align="left"><figure><img src="/files/M68xqV0jOp2vDn0zzi7u" alt="Gravity tooltip component" width="135"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from 'components/lib';

function MyComponent({ ...props }){
  return (
    <TooltipProvider>
      <Tooltip>
      
        <TooltipTrigger asChild>
          <button>Hover me</button>
        </TooltipTrigger>
        
        <TooltipContent>
          Tooltip content goes here
        </TooltipContent>
        
      </Tooltip>
    </TooltipProvider>
  );
}
```

### Props

| Prop       | Description                            | Required | Value                |
| ---------- | -------------------------------------- | -------- | -------------------- |
| className  | custom styling                         | optional | SCSS or Tailwind     |
| children   | component(s) to wrap within tooltip    | optional | component(s)         |
| sideOffset | offset of the tooltip from the trigger | optional | integer (default: 4) |

### Notes

* The `Tooltip` component uses `TooltipPrimitive` from `@radix-ui/react-tooltip` for tooltip functionality.
* The `className` prop allows for custom styling to be applied.
* The `children` prop is required and should include the trigger and content for the tooltip.
* The `sideOffset` prop specifies the offset of the tooltip from the trigger and defaults to 4.
* For more details, refer to the [Shadcn Tooltip documentation](https://ui.shadcn.com/docs/components/tooltip).


# User

The `User` component displays the current user's name and avatar. If the user belongs to more than one account, they can switch accounts here. It also provides options to change the language, toggle dark mode, and sign out.

It's rendered within the header component and appears at the top right-hand corner of the viewport.&#x20;

### Preview

<div align="left"><figure><img src="/files/P0vvKZtzbCyaiwQwrGwd" alt="Gravity user component" width="334"><figcaption></figcaption></figure></div>

### Usage

```javascript
import { User } from 'components/lib';

function MyComponent({ ...props }) {
  return (
    <div>
      <User />
    </div>
  );
}
```

### Props

This component does not accept any props directly as it uses context for its functionality.

### User Avatars

Users can upload an avatar in the `/account/profile` view, and it will be [uploaded to your S3 bucket](/gravity-server/file-uploads) in a folder called `avatars` and displayed in the `User` component.&#x20;

You can change the folder and size of the photos inside the [config](/gravity-server/config) file in the `avatar` section.

Users signed in via a social network will display the profile picture from that network unless they overwrite it by uploading a photo.&#x20;

### Notes

* The `User` component relies on `AuthContext` and `ViewContext` for authentication and view management.
* The `DropdownMenu` component is used to display the user options.


# View

The `View` component houses global components common to all views, such as notifications and dialogs. It handles errors, sets the document title, and renders the specified layout. The view and its props are rendered by the router.

{% hint style="info" %}
Props are passed to this component automatically from the [router](/gravity-web/routing).&#x20;
{% endhint %}

### Usage

```javascript
import { View } from 'components/lib';
import { DashboardView } from 'views/dashboard';

function MyApp() {
  return (
    <View
      title="Dashboard"
      layout="app"
      display={ DashboardView }
      data={ dashboardData }
    />
  );
}
```

## Props

| Prop    | Description             | Required | Value                                |
| ------- | ----------------------- | -------- | ------------------------------------ |
| display | view component          | required | component                            |
| layout  | layout component to use | required | string (account/app/auth/onboarding) |
| title   | document title          | required | string                               |

### Notes

* The `View` component relies on `ViewContext` for managing notifications, dialogs, and error handling.
* The component uses `useTranslation` for internationalization and `useToast` for toast notifications.
* Layouts include `AppLayout`, `AuthLayout`, `AccountLayout`, and `OnboardingLayout`.


# Views

Gravity has a custom [`<View>`](/gravity-web/components/view) component that serves two purposes:

1. Houses global components that are common to all views like [notification](/gravity-web/components/notification) and [modal](/gravity-web/components/modal).
2. [Handles errors](/gravity-web/handling-errors)

The `View` component is a wrapper around all of your UI views and can show a [modal](/gravity-web/components/modal), create a [banner notification](/gravity-native/components/notification) or [handle an error](/gravity-web/handling-errors) from anywhere in your application.

### Creating New Views

To add a new view to your application, you add a new view file in the `/client/src/views` directory and [add a new route](/gravity-web/routing#defining-a-new-route) in Node.js.

In Next.js you create a new page.jsx file inside the /src/app folder.

You can also generate new views quickly using the [toolbelt.](/gravity-server/cli-toolbelt)


# Handling Errors

Errors are handled by the [\<View>](/gravity-web/components/view) component, which has a `handleError` method stored in the `ViewContext`.\
\
You should always use a `try...catch` statement around any asynchronous code that may encounter an error. Inside your `catch` method, you can then call `context.handleError` and pass the error object.

```javascript
import { useContext } from 'react';
import { ViewContext } from 'components/lib';

function YourComponent(props){

  const viewContext = useContext(ViewContext);
  
  function doSomething(){
    try {
    
      ...
      
    }
    catch (err){
    
      viewContext.handleError(err);
      
    }
  }
}
```

This will display a banner notification along the top of the view with the error message text and also a console log.




---

[Next Page](/llms-full.txt/1)

