Adding Custom Branding to a User App
Tags: react, javascript, images
Published: 2021-09-14 13:13:53

---
title: Adding Custom Branding to a User App
published: true
description: You might end up working on a project with multiple users who want different layouts for their dashboards. Users typically like to have some level of customization over the applications they use and we're going to cover how to do that with Redwood.
tags: #react, #javascript, #images
cover_image: https://cdn.sanity.io/images/5ad74sb4/production/120fd0e6eb0e1e017a105e7a93720001fb6fac3c-1000x420.png?w=2000&h=2000&fit=max
---

Many organizations want some ability to use a service to handle some of their functionality and customize the interface users are shown. This includes things like the names they see displayed, data they want to be shown, or some images they want to see. Giving them the ability to add their own branding is one way to add value to your own products.

In this Redwood tutorial, we'll make an app that will change formats depending on what user is associated with the page.

## Create the Redwood app

First thing we need to do is spin up a new app. In a terminal, run:

```yarn
yarn create redwood-app branding
```

When this is done, you'll have a bunch of new files and folders in a `branding` directory. The main folders we'll be working in are the `api` and `web` folders. We'll start with some work in the `api` folder first.

## Setting up the model

Building our app by making the models for the database schema works really well in Redwood. I usually like to start here because it's one way you can start thinking about your business logic from the beginning.

We'll be using a Postgres database. Here are [the docs to get Postgres installed locally](https://www.postgresql.org/download/). Let's start by updating the `.env` file with a connection string for your local instance. Uncomment the `DATABASE_URL` line and update the value. It might look something like this.

```bash
DATABASE_URL=postgres://admin:password@localhost:5432/branding
```

Now we can go to `api > db` and open the `schema.prisam` file. This is where we'll add our models. One thing we need to do is update the `provider` value at the top to `postgresql` instead of `sqlite`. Next, you can delete the existing example model and add these.

```javascript
model User {
  id     Int      @id @default(autoincrement())
  name   String
  info   Info[]
  image  Image[]
  layout Layout[]
}

model Image {
  id     Int    @id @default(autoincrement())
  name   String
  url    String
  user   User   @relation(fields: [userId], references: [id])
  userId Int
}

model Info {
  id        Int      @id @default(autoincrement())
  balance   Float
  lastLogin DateTime
  endDate   DateTime
  user      User     @relation(fields: [userId], references: [id])
  userId    Int
}

model Layout {
  id           Int    @id @default(autoincrement())
  name         String
  dataLocation String
  imageUrl     String
  user         User   @relation(fields: [userId], references: [id])
  userId       Int
}
```

Usually, when you have relationships between tables like we have here, it's a good idea to seed your database with some initial values. You'll see this pretty often with apps that have dropdown menus or pre-defined user roles.

We'll be adding our own seed data in the `seed.js` file. You can open that and delete all of the commented-out code in the `main` function and replace it with this.

```javascript
await db.user.create({
  data: { name: 'Nimothy' },
})

await db.image.create({
  data: {
    name: 'Nimothy Profile',
    url: 'https://res.cloudinary.com/milecia/image/upload/v1606580774/fish-vegetables.jpg',
    userId: 1,
  },
})

await db.info.create({
  data: {
    balance: 7.89,
    lastLogin: new Date(),
    endDate: new Date(),
    userId: 1,
  },
})

await db.layout.create({
  data: {
    name: 'MidLeft',
    dataLocation: 'mid-left',
    imageUrl:
      'https://res.cloudinary.com/milecia/image/upload/v1606580774/fish-vegetables.jpg',
    userId: 1,
  },
})
```

### Run migration

With our models and seed data in place, we can migrate the database with this command:

```bash
yarn rw prisma migrate dev
```

That will add the tables and columns with the defined relationships to your Postgres instance. To seed the database, we'll need to run:

```bash
yarn rw prisma db seed
```

This will add the placeholder data we created in `seed.js` so that the relationships between tables and columns are met and don't cause errors with our app.

Since we've run the migration and seeding, we can move on to the back-end and front-end.

## Making the back-end and front-end

We're going to make the functionality to add new layouts and new users to the app for now so that we can show how things update for the user. We'll also be adding a special page to show how these updates would actually affect users.

For the sake of this project, we're going to assume that adding new users and layouts is admin functionality that users of the app won't be able to see. Later on, we'll add the user view that applies the custom branding.

Adding the ability to create and update users and layouts only takes a couple of commands in Redwood. Let's start by making the users functionality with this:

```bash
yarn rw g scaffold user
```

This will generate the back-end GraphQL types and resolvers as well as adding new components to the front-end. We'll run this command one more time for the layouts functionality:

```bash
yarn rw g scaffold layout
```

You can take a look at the code Redwood generated to make all of this work on the front-end by going through the `web > src` directory. There are new files under `components`, `layouts`, and `pages`, plus `Routes.js` has been updated. All of the new files you see were created by that `scaffold` command for those two models.

The back-end code that supports new user and layout creation and the edit and delete functionality can be found in the `api > src` directory. You'll see new files under `graphql` and `services` that hold the GraphQL types and resolvers that make all of the CRUD work and persists the data.

Now we have the CRUD for the front-end and back-end for these two models. You can run the `scaffold` command to create the CRUD for the other models, but we don't actually need it. What we _do_ need are the types for those models. We can generate those with a couple of Redwood commands:

```bash
yarn rw g sdl info
yarn rw g sdl image
```

The `sdl` generator makes all of the GraphQL types and a resolver for the specified model. If you check out `api > src > graphql`, you'll see the new types that were generated for info and images. Then if you look in `api > src > service`, you'll see that a resolver has been made to handle a query for us for both info and images.

The reason we're adding these types is so the user types reference these so we need them to be available, even if we aren't adding the front-end piece.

### Running the updated app

If you run your app with `yarn rw dev` and navigate to `localhost:8910/users`, you'll see a table and buttons for different ways to interact with the data. You should see something similar to this:

![users table](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041441/e-603fc55d218a650069f5228b/kkzg6tzgiga2jdbpx9mm.png)

Go ahead and add a new user by clicking the "New User" button. This will open the form like this:

![new user form](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041426/e-603fc55d218a650069f5228b/dzfe2pj48gskppaxa2nl.png)

Now you can add a new layout for this new user by going to `localhost:8910/layouts` and clicking the "New Layout" button. It'll bring up this form:

![new layout form](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041413/e-603fc55d218a650069f5228b/vcbn7kdin9nm45t1ekg0.png)

## Show the user their custom view

Now that we've got the core functionality together to create users and associate layouts with them, we can create the custom view that they will see. To do that, we'll use Redwood to generate a page that will load a specific user's layout. Make a new page with this command:

```bash
yarn rw g page option
```

This will add a new page to the `web > src > pages` directory and it will update the `Routes.js` file with a new `/option` route. If you navigate to `localhost:8910/option`, you'll see this:

![option page](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041396/e-603fc55d218a650069f5228b/vwrmwymhxmgspzhgc2sx.png)

We need to update this page to show the user's layout by pulling some data from the back-end.

### Querying for the user layout

In the `web > src > pages > OptionPage` directory, open the `OptionPage.js` file and add the following import to get your GraphQL query ready.

```javascript
import { useQuery } from '@redwoodjs/web'
```

Then at the bottom of the file, right above the export statement, add this code for the query.

```javascript
const LAYOUT = gql`
  query layout($id: Int!) {
    layout(id: $id) {
      id
      name
      dataLocation
      imageUrl
      userId
    }
  }
`
```

This will give us a specific layout based on the id we pass to the query. We'll be manually setting this id to mimic what we might get from a prop from a different component. We'll add the variable for the id in our query hook. This will be added inside of the `OptionPage` component:

```javascript
const { loading, data } = useQuery(LAYOUT, {
  variables: { id: 1 },
})

if (loading) {
  return <div>Loading...</div>
}
```

We're using the `useQuery` hook to execute the query we made earlier and we're manually setting the id of the layout we want to use. Then we're checking the load status for the data and rendering an indicator that the page is loading the content so that the user doesn't see an error before the fetch finishes.

The last thing we'll do is update the elements to show in the layout format we currently have loaded.

### Updating the page

To show the right layout, we're going to install the `styled-components` package. That way we'll be able to pass props to update the layout based on the user viewing the page. So in the `web` directory in your terminal, run:

```bash
yarn add styled-components
```

Now we're going to import that package in the `OptionPage.js` file.

```javascript
import styled from 'styled-components'
```

Then we need to add a new styled component to handle the location of the image based on that user layout. We'll add this right above the `OptionPage` component.

```javascript
const Img = styled.img`
  display: block;
  position: absolute;
  top: ${(props) => (props.dataLocation === 'mid-left' ? '35%' : 'unset')};
  right: ${(props) => (props.dataLocation === 'mid-left' ? 'unset' : '0')};
  width: 360px;
`
```

We're doing a simple update of the image location with an absolute position setup. This will let the image move independently of the other elements on the page so that the user sees it in the place they've selected. We're passing in the `dataLocation` value as a prop.

### Cleaning things up

Just a few finishing touches and we'll have this layout working. First, we need to add the `Img` to `OptionPage`. We'll delete the existing `Link` from the return statement and add this image instead.

```javascript
<Img src={data.layout.imageUrl} dataLocation={data.layout.dataLocation} />
```

We'll also add a little line to show the name of the current layout. This will go below the description of the file location.

```javascript
<p>{data.layout.name}</p>
```

That's it! We've finished up this app. Now if you run the app with `yarn rw dev`, you should see something similar to this.

![user one layout](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041358/e-603fc55d218a650069f5228b/zcw2tdqrwyur6bpzryv4.png)

If you update the `id` in the query variable to `2` and reload the browser, you'll see something like this.

![user two layout](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627041339/e-603fc55d218a650069f5228b/nhs5ar1x8kyeppxzparf.png)

## Finished code

If you want to check out the complete code, you can check it out in the [`custom-app-branding` folder of this repo](https://github.com/flippedcoder/blog-examples/tree/main/custom-app-branding). You can also check out the front-end in [this Code Sandbox](https://codesandbox.io/s/new-sound-pp7z3).

## Conclusion

If you're interested in a deeper dive on how Redwood handles scaffolding or the general way it creates files for you, make sure to go through [their docs](https://redwoodjs.com/docs/introduction).