Redwood, a new framework
Tags: webdev, javascript, redwood, react
Published: 2022-07-07 11:47:15

by [Ejiro Thankgod](https://blog.openreplay.com/authors/ejiro-thankgod)

According to its [documentation](https://redwoodjs.com/docs/introduction), 

> "Redwood is a full-stack web development framework designed to help you grow from side project to startup."

Redwood features an end-to-end development workflow that weaves together the best part of [React](https://reactjs.org/) for developing pages and components, [GraphQL](https://graphql.org/) as the query language for data, [Prisma](https://www.prisma.io/) as ORM for accessing data, [TypeScript](https://www.typescriptlang.org/) for better programming, [Jest](https://jestjs.io/) for testing, and [Storybook](https://storybook.js.org/) to aid with component development. It provides a full-stack development process in [JAMStack](https://jamstack.org/).

[Redwood](https://redwoodjs.com/) is obsessed with developer experience and eliminating as much boilerplate as possible where existing libraries elegantly solve these problems, and where we don't write our solution. The result has a Javascript development experience one can fall in love with!

## Features

Let's look at Redwood's main basic features that make it look attractive as a full-fledged framework.

- Project Structure: Two folders are generated as API and Web when creating a Redwood project. API folder contains the GraphQL back end of your project, which runs on any server. The Web folder contains React front-end code; Redwood uses React for the front end, and several features are built around Routing, Components, Forms, and Cells, including unique formatting structures.

- Distinctive Command-Line: RedwoodJS CLI allows you to generate any file you need in the directory you specify. It includes two entry points, `rw` and `rwt`, which stand for developing an application and contributing to the frameworks, respectively. Also, you can use the CLI for migration and scaffolding purposes.

- Routing: Redwood Router is a customized version of React Router. It allows developers to track Routes with their corresponding pages by listing all the Routes in a single Routing file. The **Named Routes** feature allows you to add a reference name to each Route as you prefer. There are three main prop types in React Router name (used to call the route component from the link component), path (used to match the URL path to the route), and page (a prop that indicates the relevant component that needs to be rendered when the path is matched).

```javascript
// web/src/Routes.js 
import { Router, Route } from ‘@redwoodjs/router’ 
const Routes = () => { 
    return ( 
    <Router> 
        <Route path=”/” page={HomePage} name=”home” /> 
        <Route path=”/users/{type}” page={UsersPage} name=”users” />     </Router> 
    ) 
}
// web/src/components/Admin/Admin.js 
import { Link, routes } from ‘@redwoodjs/router’ 
const Admin = () => { 
    return ( 
      <h1><Link to={routes.home()}>My CRM</Link></h1> 
      <Link to={routes.users({type: “admin”})}>View Admins</Link> 
    ) 
}
```

- Data Fetch with Cells: RedwoodJS cells are considered higher-level components that can handle data for you. If you are using RedwoodJS cells, you probably need to write the query then Redwood will do the rest for you. The data fetching mechanism is quite different from the typical traditional system. Redwood provides an asynchronous tool to fetch data where the front end and back end process their task separately, which makes the front end load without waiting for the data to be fetched. Let's look at Redwood cells; they contain four states named `Loading`, `Empty`, `Error`, and `Success`, which are automatically rendered depending on the state of your cell.

```javascript
// web/src/components/UsersCell/UsersCell.js
export const QUERY = gql`
  query USERS {
    users {
      id
      name
    }
  }

export const Loading = () => <div>Loading users...</div>
export const Empty = () => <div>No users yet!</div>
export const Failure = ({ message }) => <div>Error: {message}</div>
export const Success = ({ users }) => {
  return (
    <ul>
      { users.map(user => (
        <li>{user.id} | {user.name}</li>
      ))}
    </ul>
  )
}
```

- Integration with regular HTML: Redwood has identified the issue of React Forms and its complexities compared to a typical HTML Form, and has provided a solution which is a helper method as a wrapper to React Forms, around `react-hook-form`. With this method, it's easier to validate client & server-side where you need to put the required styles to the imported input fields as follows:

```javascript
import { Form, Label, TextAreaField, FieldError, Submit } from “@redwoodjs/web” 
export const Comment = () => { 
  const onSubmit = (data) => { 
    console.info(`Submitted: ${data}`) 
  } 
  return ( 
    <Form onSubmit={onSubmit}> <Label name=”comment” errorStyle={{ color: “red” }} /> 
      <TextAreaField 
        name=”comment” 
        errorStyle={{ borderColor: “red” }} 
        validation={{ required: true }} 
      /> 
      <FieldError name=”comment” errorStyle={{ color: “red” }} />
      <Submit>Send</Submit> 
    </Form> 
  ) 
}
```


## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._



## How Redwood works

In a typical setting, Redwood applications are split into two parts: a front end and a back end. This is represented as two JS/TS projects within a single monorepo. The front-end project is called the web side, and the back-end project is called the API side. The web side will end up running in the user's browser, while code on the API side will run on the server.

*Note: Redwood refers to 'front end' as 'web'. This is because Redwood conceives of a world where you may have other sides like 'mobiles', 'desktop', 'cli', etc. But for the sake of this article, we'll be referring to it as the front end.*

The front end is built with React. Redwood's router makes it simple to map URL paths to React 'Page' components. Pages may contain a 'Layout' component to wrap content and 'Cells' and regular React components. The Cells allow you to declaratively manage the lifecycle of a component that fetches and displays data. Other Redwood utility components make it insignificant to integrate forms and various basic needs.

The back end is an implementation of GraphQL API. Your business logic is organized into 'services' that represent their internal API and can be called both from external GraphQL requests and other internal services. Redwood can automatically connect your internal services with Apollo, reducing the amount of boilerplate you have to write.

Before we move on to building our Redwood project, let's take a look at some of the basic commands of RedwoodJS CLI:

- `yarn rw g page`: generates a page component
- `yarn rw prisma migrate dev`: Runs migrations on our application
- `yarn rw g sdl`: generates a GraphQL schema definition file
- `yarn rw g service`: generates the service files
- `yarn rw g cell`: generates cell component
- `yarn rw dev`: runs Redwood dev server on [http://localhost:8910/](http://localhost:8910/).

## Building an app

We need to illustrate how to use RedwoodJS to build an app. The Todo app will be able to take notes and save those notes on the JSON server.
*Note: Credit to RedwoodJS for the [example](https://github.com/redwoodjs/example-todo).*

### First steps

To create the project, all we have to do is run the following commands:

```
yarn create redwood-app todo-app
cd todo-app
yarn rw dev
```

![1](https://blog.openreplay.com/static/fa83f6f57c279b729914ea75aecb65c7/1a152/image01.png)

Let's take a glance at our folder structure:

```
    todo-app
    │
    ├── api
    │   ├── db
    │   │   ├── schema.prisma
    │   │   └── seed.js
    │   └── src
    │       ├── functions
    │       │   └── graphql.js
    │       ├── graphql
    │       ├── lib
    │       │   └── db.js
    │       └── services
    └── web
        ├── public
        │   ├── README.md
        │   ├── favicon.png
        │   └── robots.txt
        └── src
            ├── Routes.js
            ├── components
            ├── index.css
            ├── index.html
            ├── App.js
            ├── layouts
            └── pages
                ├── FatalErrorPage
                │   └── FatalErrorPage.js
                └── NotFoundPage
                    └── NotFoundPage.js
```

### Defining data models

Now we have to add our Todo model to the schema.prisma file.

```
model Todo {
  id     Int    @id @default(autoincrement())
  body   String
  status String @default("off")
}
```

Note: Run `yarn rw prisma migrate`, which will prompt for the name of the migrations; just type added_todo and hit the enter button. Redwood will create and run the migrations.

We'll create services that will be used to carry out CRUD operations on our todo table in the db:

```
yarn rw g service todo
yarn rw g sdl
```

This command will create the files:
- api/src/graphql/todos.sdl.js: contains the GraphQL schema definitions of our GraphQL endpoints.
- api/src/services/todos/todos.js: our service file contains all query and mutation resolver functions.

### Creating the page and components

Our app will have just a page, i.e., the HomePage. The homepage will show the to-dos in the database and the input field.

```
yarn rw g page home/ todos
```

This command creates the to-do page. The main file is located at web/src/pages/HomePage/HomePage.js. The page will map to the URL path "/" home. We'll also create a TodoListCell: `yarn rw g cell list todo`. It will generate files in the components folder. The main file is located at web/src/components/TodoListCell/TodoListcell.js.

We modify the code to this:

```javascript
import styled from 'styled-components'
import TodoItem from 'src/components/TodoItem'
import { useMutation } from '@redwoodjs/web'
export const QUERY = gql`
  query TODOS {
    todos {
      id
      body
      status
    }
  }
`
const UPDATE_TODO_STATUS = gql`
  mutation TodoListCell_CheckTodo($id: Int!, $status: String!) {
    updateTodoStatus(id: $id, status: $status) {
      id
      __typename
      status
    }
  }
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div></div>
export const Failure = () => <div>Oh no</div>
export const Success = ({ todos }) => {
  const [updateTodoStatus] = useMutation(UPDATE_TODO_STATUS)
  const handleCheckClick = (id, status) => {
    updateTodoStatus({
      variables: { id, status },
      optimisticResponse: {
        __typename: 'Mutation',
        updateTodoStatus: { __typename: 'Todo', id, status: 'loading' },
      },
    })
  }
  const list = todos.map((todo) => (
    <TodoItem key={todo.id} {...todo} onClickCheck={handleCheckClick} />
  ))
  return <SC.List>{list}</SC.List>
}
export const beforeQuery = (props) => ({
  variables: props,
})
const SC = {}
SC.List = styled.ul`
  padding: 0;
  `

```

We'll generate  `AddTodoControl` component, which will have an input form with a placeholder and a submit button to submit our todos. Now, let's scaffold the AddTodoControl component:

```
yarn rw g component AddTodoControl
```

It creates a `AddTodocontrol` component located at web/src/components/AddTodoControl/AddTodoControl.js and our code should look like this:

```javascript
import styled from 'styled-components'
import { useState } from 'react'
import Check from 'src/components/Check'
const AddTodoControl = ({ submitTodo }) => {
  const [todoText, setTodoText] = useState('')
  const handleSubmit = (event) => {
    submitTodo(todoText)
    setTodoText('')
    event.preventDefault()
  }
  const handleChange = (event) => {
    setTodoText(event.target.value)
  }
  return (
    <SC.Form onSubmit={handleSubmit}>
      <Check type="plus" />
      <SC.Body>
        <SC.Input
          type="text"
          value={todoText}
          placeholder="Memorize the dictionary"
          onChange={handleChange}
        />
        <SC.Button type="submit" value="Add Item" />
      </SC.Body>
    </SC.Form>
  )
}
const SC = {}
SC.Form = styled.form`
  display: flex;
  align-items: center;
`
SC.Body = styled.div`
  border-top: 1px solid #efefef;
  border-bottom: 1px solid #efefef;
  width: 100%;
`
SC.Input = styled.input`
  border: none;
  font-size: 18px;
  font-family: 'Inconsolata', monospace;
  padding: 10px 0;
  width: 75%;

  ::placeholder {
    color: #9e9595;
  }
`
SC.Button = styled.input`
  float: right;
  margin-top: 5px;
  border-radius: 6px;
  background-color: #f75d52;
  padding: 5px 15px;
  color: white;
  border: 0;
  font-size: 18px;
  font-family: 'Inconsolata', monospace;
  :hover {
    background-color: black;
    cursor: pointer;
  }
`
export default AddTodoControl
```    

We’ll create our TodoItem with the command `yarn rw g component TodoItem` and this component consists of our style attributes for each todo body, so we’re going to implement things like padding, borders, and lines with the code below:

```javascript
import styled from 'styled-components'
import Check from 'src/components/Check'
const TodoItem = ({ id, body, status, onClickCheck }) => {
  const handleCheck = () => {
    const newStatus = status === 'off' ? 'on' : 'off'
    onClickCheck(id, newStatus)
  }
  return (
    <SC.Item>
      <SC.Target onClick={handleCheck}>
        <Check type={status} />
      </SC.Target>
      <SC.Body>{status === 'on' ? 
{body}
 : body}</SC.Body>
    </SC.Item>
  )
}
const SC = {}
SC.Item = styled.li`
  display: flex;
  align-items: center;
  list-style: none;
`
SC.Target = styled.div`
  cursor: pointer;
`
SC.Body = styled.div`
  list-style: none;
  font-size: 18px;
  border-top: 1px solid #efefef;
  padding: 10px 0;
  width: 100%;
`
export default TodoItem
```    

The Check component consists of our icons like on, off, plus icons. To create the check component, run `yarn rw g component`. Input the following code in our Check component:

```javascript
import styled from 'styled-components'
import IconOn from './on.svg'
import IconOff from './off.svg'
import IconPlus from './plus.svg'
import IconLoading from './loading.svg'
const map = {
  on: <IconOn />,
  off: <IconOff />,
  plus: <IconPlus />,
  loading: <IconLoading />,
}
const Check = ({ type }) => {
  return <SC.Icon>{map[type]}</SC.Icon>
}
const SC = {}
SC.Icon = styled.div`
  margin-right: 15px;
`
export default Check
```    

We need an AddTodo component to add new todos. 

```javascript
import { useMutation } from '@redwoodjs/web'
import AddTodoControl from 'src/components/AddTodoControl'
import { QUERY as TODOS } from 'src/components/TodoListCell'

const CREATE_TODO = gql`
  mutation AddTodo_CreateTodo($body: String!) {
    createTodo(body: $body) {
      id
      __typename
      body
      status
    }
  }
`
const AddTodo = () => {
  const [createTodo] = useMutation(CREATE_TODO, {

    update: (cache, { data: { createTodo } }) => {
      const { todos } = cache.readQuery({ query: TODOS })
      cache.writeQuery({
        query: TODOS,
        data: { todos: todos.concat([createTodo]) },
      })
    },
  })
  const submitTodo = (body) => {
    createTodo({
      variables: { body },
      optimisticResponse: {
        __typename: 'Mutation',
        createTodo: { __typename: 'Todo', id: 0, body, status: 'loading' },
      },
    })
  }
  return <AddTodoControl submitTodo={submitTodo} />
}
export default AddTodo
```    

This is our final project.

![Redwood final project](https://blog.openreplay.com/static/0b993839230d7ad6367d7c137b7e0cac/1a152/image02.png)


## Conclusion

There are many other reasons to get attracted to RedwoodJS.

- Opinionated defaults for formatting, file organization, Webpack, Babel, and more
- Generators for pages, layouts, cells, SDL, services, etc.
- Forms with easy client-and/or server-side validation and error handling
- Database and Data migrations

All these and many more make RedwoodJS unique to the core. 

## Resources
- [Github/redwood](https://github.com/redwoodjs/redwood)
- [Redwood](https://redwoodjs.com/)



[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)