Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
June 16, 2022 03:24 pm GMT

Building a multi-step onboarding flow in 5 minutes

In this post I will show you how easy it to build a multi-step onboarding flow with Saas UI.

Saas UI is a component library build on top of Chakra UI and contains a lot of components currently missing in Chakra UI as well as higher order components that help boost your productivity.

You typically find onboarding flows during or after signing up for a new product and can consist of a couple of steps to help you setup your new account.

The tech

The stack that we will be using:

  • Saas UI
  • Chakra UI
  • Next.js
  • React Hook Form (used internally by Saas UI)

The requirements

For this example we will be building a form with 3 steps. It should have a stepper to indicate in which step the user currently is, validation and it should work well on small screens.

Information

Here we will ask for some personal information as well as business information.

First name, last name, company, if a company name is entered, ask for the company size.

Create workspace

Workspace name and url

Invite team members

Be able to enter multiple email addresses.

1. Installation

I've prepared a starter repository to help you get started quickly. Get it from Github

git clone [email protected]:saas-js/saas-ui-nextjs-typescript.git

After closing let's install all dependencies.

yarn

Existing project

If you want to continue in an existing Next.js project you can run this to install all required dependencies.

yarn add @saas-ui/react @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6

2. Create the onboarding page

So first thing we need to do is create a page for the onboarding flow.

Create a new page; pages/onboarding.tsx

import { Box } from '@chakra-ui/react'import { NextPage } from 'next'const OnboardingPage : NextPage = () => {  return (    <Box>      Onboarding    </Box>  )}export default OnboardingPage

Now open your browser and visit:

https://localhost:3000/onboarding

If everything went right, you should see Onboarding now.

Now let's make the page look a bit better by adding a title and let's create the first form step.

import { Container, Heading, VStack } from '@chakra-ui/react'import {  Card,  CardBody,  Field,  FormLayout,  FormStep,  NextButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px">          <FormStep name="information">            <Card>              <CardBody>                <FormLayout>                  <Field name="firstName" label="First name" />                  <Field name="lastName" label="Last name" />                  <NextButton />                </FormLayout>              </CardBody>            </Card>          </FormStep>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

Looks good already doesn't it? We now have a fully working multi step form. Try it out by entering some information and pressing complete. You should see the values in the console, ready to post to your backend.

StepForm (and Form) use React Hook Form internally to manage the form state, it allows you to build forms super fast without a lot of boilerplate.

Now let's add the rest of the fields and the other 2 steps to make it truly multi step.

import { ButtonGroup, Container, Heading, VStack } from '@chakra-ui/react'import {  Card,  CardBody,  Field,  FormLayout,  FormStep,  NextButton,  PrevButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px">          <FormStep name="information">            <Card>              <CardBody>                <FormLayout>                  <FormLayout columns={2}>                    <Field name="firstName" label="First name" />                    <Field name="lastName" label="Last name" />                  </FormLayout>                  <Field name="company" label="Company name" />                  <NextButton />                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="workspace">            <Card>              <CardBody>                <FormLayout>                  <Field name="name" label="Workspace name" />                  <Field name="url" label="Workspace url" />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="invite">            <Card>              <CardBody>                <FormLayout>                  <Field                    name="email"                    label="Invite your teammembers"                    help="Add multiple addresses by separating them with a comma (,)"                    type="textarea"                  />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

Awesome we can now move back and forth through our steps. When you complete the last step you should see all your field values being logged in the console. Sweet!

The next step is to add some validation, because we can complete all steps now without entering any information.

import { ButtonGroup, Container, Heading, VStack } from '@chakra-ui/react'import {  Card,  CardBody,  Field,  FormLayout,  FormStep,  NextButton,  PrevButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px" noValidate>          <FormStep name="information">            <Card>              <CardBody>                <FormLayout>                  <FormLayout columns={2}>                    <Field                      name="firstName"                      label="First name"                      isRequired                      rules={{ required: 'Please enter your first name.' }}                    />                    <Field                      name="lastName"                      label="Last name"                      isRequired                      rules={{ required: 'Please enter your last name.' }}                    />                  </FormLayout>                  <Field name="company" label="Company name" />                  <NextButton />                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="workspace">            <Card>              <CardBody>                <FormLayout>                  <Field                    name="name"                    label="Workspace name"                    isRequired                    rules={{ required: 'Please enter a name ' }}                  />                  <Field                    name="url"                    label="Workspace url"                    help="We will create one for you if you leave this empty."                  />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="invite">            <Card>              <CardBody>                <FormLayout>                  <Field                    name="emails"                    label="Invite your teammembers"                    help="Add multiple addresses by separating them with a comma (,)"                    type="textarea"                  />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

That was easy! rules accepts all React Hook Form rules and the form also accepts schema resolvers, but more on this in another post.

Note that we added noValidate to the form, to disable the native browser validation messages.

Ok, so we want to ask a little bit more information when a company signed up. Let's add this now, we can simply do this with the DisplayIf component. We'll add a custom Select with the company size options.

import { ButtonGroup, Container, Heading, VStack } from '@chakra-ui/react'import {  Card,  CardBody,  DisplayIf,  Field,  FormLayout,  FormStep,  NextButton,  PrevButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px" noValidate>          <FormStep name="information">            <Card>              <CardBody>                <FormLayout>                  <FormLayout columns={2}>                    <Field                      name="firstName"                      label="First name"                      isRequired                      rules={{ required: 'Please enter your first name.' }}                    />                    <Field                      name="lastName"                      label="Last name"                      isRequired                      rules={{ required: 'Please enter your last name.' }}                    />                  </FormLayout>                  <Field name="company" label="Company name" />                  <DisplayIf name="company">                    <Field                      name="companySize"                      label="Company size"                      placeholder="Select your company size"                      type="select"                      options={[                        {                          value: '1',                          label: '1 to 5',                        },                        {                          value: '5',                          label: '5 to 20',                        },                        {                          value: '20',                          label: '20 or more',                        },                      ]}                    />                  </DisplayIf>                  <NextButton />                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="workspace">            <Card>              <CardBody>                <FormLayout>                  <Field                    name="name"                    label="Workspace name"                    isRequired                    rules={{ required: 'Please enter a name ' }}                  />                  <Field                    name="url"                    label="Workspace url"                    help="We will create one for you if you leave this empty."                  />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>          <FormStep name="invite">            <Card>              <CardBody>                <FormLayout>                  <Field                    name="emails"                    label="Invite your teammembers"                    help="Add multiple addresses by separating them with a comma (,)"                    type="textarea"                  />                  <ButtonGroup>                    <NextButton />                    <PrevButton />                  </ButtonGroup>                </FormLayout>              </CardBody>            </Card>          </FormStep>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

Bam! Conditional fields without any difficult logic or if/else statements.

Now we still miss an important part, the Stepper, let's add this now. We can simply wrap the steps with the FormStepper component and add a title to the steps.

import { ButtonGroup, Container, Heading, VStack } from '@chakra-ui/react'import {  Card,  CardBody,  DisplayIf,  Field,  FormLayout,  FormStep,  FormStepper,  NextButton,  PrevButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px" noValidate>          <FormStepper>            <FormStep name="information" title="Information">              <Card>                <CardBody>                  <FormLayout>                    <FormLayout columns={2}>                      <Field                        name="firstName"                        label="First name"                        isRequired                        rules={{ required: 'Please enter your first name.' }}                      />                      <Field                        name="lastName"                        label="Last name"                        isRequired                        rules={{ required: 'Please enter your last name.' }}                      />                    </FormLayout>                    <Field name="company" label="Company name" />                    <DisplayIf name="company">                      <Field                        name="companySize"                        label="Company size"                        placeholder="Select your company size"                        type="select"                        options={[                          {                            value: '1',                            label: '1 to 5',                          },                          {                            value: '5',                            label: '5 to 20',                          },                          {                            value: '20',                            label: '20 or more',                          },                        ]}                      />                    </DisplayIf>                    <NextButton />                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="workspace" title="Workspace">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="name"                      label="Workspace name"                      isRequired                      rules={{ required: 'Please enter a name ' }}                    />                    <Field                      name="url"                      label="Workspace url"                      help="We will create one for you if you leave this empty."                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="invite" title="Invite team">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="emails"                      label="Invite your teammembers"                      help="Add multiple addresses by separating them with a comma (,)"                      type="textarea"                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>          </FormStepper>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

The last requirement we have is to make the form work well on small screens. Luckily the Stepper supports a vertical orientation, we can use this in combination with useBreakpointValue. We'll also make sure the first and last name is rendered under each other on mobile screens.

import {  ButtonGroup,  Container,  Heading,  useBreakpointValue,  VStack,} from '@chakra-ui/react'import {  Card,  CardBody,  DisplayIf,  Field,  FormLayout,  FormStep,  FormStepper,  NextButton,  PrevButton,  StepForm,} from '@saas-ui/react'import { NextPage } from 'next'const OnboardingPage: NextPage = () => {  const onSubmit = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm onSubmit={onSubmit} width="420px" noValidate>          <FormStepper            orientation={useBreakpointValue({              base: 'vertical',              md: 'horizontal',            })}          >            <FormStep name="information" title="Information">              <Card>                <CardBody>                  <FormLayout>                    <FormLayout columns={{ base: 1, md: 2 }}>                      <Field                        name="firstName"                        label="First name"                        isRequired                        rules={{ required: 'Please enter your first name.' }}                      />                      <Field                        name="lastName"                        label="Last name"                        isRequired                        rules={{ required: 'Please enter your last name.' }}                      />                    </FormLayout>                    <Field name="company" label="Company name" />                    <DisplayIf name="company">                      <Field                        name="companySize"                        label="Company size"                        placeholder="Select your company size"                        type="select"                        options={[                          {                            value: '1',                            label: '1 to 5',                          },                          {                            value: '5',                            label: '5 to 20',                          },                          {                            value: '20',                            label: '20 or more',                          },                        ]}                      />                    </DisplayIf>                    <NextButton />                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="workspace" title="Workspace">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="name"                      label="Workspace name"                      isRequired                      rules={{ required: 'Please enter a name ' }}                    />                    <Field                      name="url"                      label="Workspace url"                      help="We will create one for you if you leave this empty."                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="invite" title="Invite team">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="emails"                      label="Invite your teammembers"                      help="Add multiple addresses by separating them with a comma (,)"                      type="textarea"                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>          </FormStepper>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

Without a blink!

Now we're almost ready, you probably have noticed the type warning in the onSubmit handler. Let's solve this by making the form typesafe.

Your form should something like this now.

import {  ButtonGroup,  Container,  Heading,  useBreakpointValue,  VStack,} from '@chakra-ui/react'import {  Card,  CardBody,  DisplayIf,  Field,  FormLayout,  FormStep,  FormStepper,  NextButton,  PrevButton,  StepForm,  SubmitHandler,} from '@saas-ui/react'import { NextPage } from 'next'interface InformationInputs {  firstName: string  lastName: string  company?: string  companySize: string}interface WorkspaceInputs {  name: string  url?: string}interface InviteInputs {  email?: string}type FormInputs = InformationInputs & WorkspaceInputs & InviteInputsconst OnboardingPage: NextPage = () => {  const onSubmit: SubmitHandler<FormInputs> = async (data) => {    console.log(data)  }  return (    <Container maxW="container.xl" pt="20">      <VStack spacing="8">        <Heading size="lg" textAlign="center">          Welcome to ACME Corp        </Heading>        <StepForm<FormInputs> onSubmit={onSubmit} width="420px" noValidate>          <FormStepper            orientation={useBreakpointValue({              base: 'vertical',              md: 'horizontal',            })}          >            <FormStep name="information" title="Information">              <Card>                <CardBody>                  <FormLayout>                    <FormLayout columns={{ base: 1, md: 2 }}>                      <Field                        name="firstName"                        label="First name"                        isRequired                        rules={{ required: 'Please enter your first name.' }}                      />                      <Field                        name="lastName"                        label="Last name"                        isRequired                        rules={{ required: 'Please enter your last name.' }}                      />                    </FormLayout>                    <Field name="company" label="Company name" />                    <DisplayIf name="company">                      <Field                        name="companySize"                        label="Company size"                        placeholder="Select your company size"                        type="select"                        options={[                          {                            value: '1',                            label: '1 to 5',                          },                          {                            value: '5',                            label: '5 to 20',                          },                          {                            value: '20',                            label: '20 or more',                          },                        ]}                      />                    </DisplayIf>                    <NextButton />                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="workspace" title="Workspace">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="name"                      label="Workspace name"                      isRequired                      rules={{ required: 'Please enter a name ' }}                    />                    <Field                      name="url"                      label="Workspace url"                      help="We will create one for you if you leave this empty."                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>            <FormStep name="invite" title="Invite team">              <Card>                <CardBody>                  <FormLayout>                    <Field                      name="emails"                      label="Invite your teammembers"                      help="Add multiple addresses by separating them with a comma (,)"                      type="textarea"                    />                    <ButtonGroup>                      <NextButton />                      <PrevButton />                    </ButtonGroup>                  </FormLayout>                </CardBody>              </Card>            </FormStep>          </FormStepper>        </StepForm>      </VStack>    </Container>  )}export default OnboardingPage

Great work! We got a fully working multi step form ready in just a few minutes.

You can find the fully working example here.

Now there is a lot more you can add, like hooking up the steps to the router. Adding schema validation and more advanced fields like the ArrayField to add individual email instead of a comma separated list.

Let me know what you think in the comments and what you would like to see more.

Happy coding!


Original Link: https://dev.to/eelcowiersma/building-a-multi-step-onboarding-flow-in-5-minutes-2176

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To