Overview

Do you love the JAMstack philosophy but need a database-backed web app? Want great developer experience and easy scaling? Redwood is here! Built on React, GraphQL, and Prisma, Redwood works with the components and development workflow you love, but with simple conventions and helpers to make your experience even better.

How it's Organized

Redwood places both the frontend and backend code in a single monorepo. /web contains the frontend and is served as static files through a CDN (and automatically code-split for you). /api contains the backend serverless functions (a GraphQL API by default) that your frontend will call when it needs some dynamic data.

Routing

Redwood features its own router that brings some awesome innovation to this often overlooked (but important) part of your app. Named routes let you reference a route by a name you define, route param types take care of coercing values based on data type or custom functions, and more.


import { Router, Route } from '@redwoodjs/router'
const Routes = () => {
  return (
    
      
      
    
  )
}

// web/src/components/Admin/Admin.js
import { Link, routes } from '@redwoodjs/router'
const Admin = () => {
  return (
    

My CRM

View Admins ) }

Cells

The sequence of retrieving data from an API, showing a loading placeholder, and then displaying the result is so common that Redwood codifies it into a declarative code pattern, resulting in simple and readable code! We call them cells and they contain the GraphQL query, loading, empty, error, and success states, each one rendering itself automatically depending on what state your cell is in.


export const QUERY = gql`
  query USERS {
    users {
      id
      name
    }
  }
`
export const Loading = () => 
Loading users...
export const Empty = () =>
No users yet!
export const Failure = ({ message }) =>
Error: {message}
export const Success = ({ users }) => { return (
    { users.map(user => (
  • {user.id} | {user.name}
  • ))}
) }

Services

Redwood puts all your business logic in one place—Services. These can be used by your GraphQL API or any other place in your backend code. Redwood does all of the annoying stuff for you. Check out how easy it is to create and consume a GraphQL endpoint (we didn't even omit any import statements or cheat to get the code smaller, honest!):


export const schema = gql`
  type User {
    id: Int!
    name: String!
  }
  type Query {
    users: [User]
  }
`

// api/src/services/users/users.js
export const users = () => {
  return db.user.findMany()
}

// web/src/components/Users/Users.js
export const QUERY = gql`
  query USERS {
    users {
      id
      name
    }
  }
`
export const Success = ({ users }) {
  return JSON.stringify(user)
}

Generators

Even with a relentless elimination of boilerplate, Redwood files still need a bit of code to get you started, and need to be created in the right directories. No problem, let a computer do that for you! Redwood contains several generators which create the shell of cells, pages, layouts, services, and more. We even have a scaffold generator which will create all the pages necessary to run end-to-end CRUD operations on a database table.


# Create a cell for Users
$ yarn rw generate cell Users

# Create a layout named Admin
$ yarn rw generate layout Admin

# Create an About page and set the URL to /about
$ yarn rw generate page About /about

# Read the DB schema and create the SDL file for the User table
$ yarn rw generate sdl User

# Create api/src/services/email/email.js and include CRUD functions
$ yarn rw generate service Email --crud

# Create the SDL, service, cells, pages and components to CRUD a User
$ yarn rw generate scaffold User

Forms

Working with forms is notoriously annoying in React apps. Redwood removes the complexity and gives you the freedom to work with regular HTML inputs, but sprinkled with a bit of validation and error handling. Show client and server-side errors automatically and style them like any other React component.


import { Form, Label, TextAreaField, FieldError, Submit } from "@redwoodjs/web"

export const Comment = () => {
  const onSubmit = (data) {
    console.info(`Submitted: ${data}`)
  }
  return (
    
Send
) }