Creating Your Own JAMStack Blog in No Time with Next.js and Bison
Tags: nextjs, javascript, node, jamstack
Published: 2020-10-20 14:09:08

---
title: Creating Your Own JAMStack Blog in No Time with Next.js and Bison
published: true
description: Bison is a boilerplate that wraps Next.js with Prisma, Nexus, Apollo and code generators. Shipping with it is fast. 
tags: nextjs,javascript,node,jamstack
cover_image: https://i.ibb.co/KjLqJ2W/image.png
---

Next.js is a fantastic framework for building fast and powerful JAMStack web applications — that’s one reason among many that it’s our go-to JavaScript framework at Echobind. We’ve wrapped Next.js with a set of tools we regularly use into a JAMStack generator called Bison, which makes Next.js even more powerful. In this article, I’ll show you the steps to building a custom blog with Bison and Next.js.

If you want to jump right to the code, [here’s the repo on GitHub](https://github.com/mcavaliere/bison-blog).

## Run the Bison Generator

    yarn create bison-app bison-blog

![](https://cdn-images-1.medium.com/max/5088/1*Bki-TOK09SWwTeqoVq-3vQ.png)

This will generate your app skeleton, run yarn installand create a Git repo automatically.

![](https://cdn-images-1.medium.com/max/2000/1*dJVbPJKVopuYyZvRvIw20A.png)

## Set up your database

You’ll want to [check the latest README](https://github.com/echobind/bisonapp#getting-started) for full steps, but it’s as simple as making sure PostgresSQL is running (I highly recommend [Postgres.app](https://postgresapp.com/)) and running yarn db:setup .

You’ll be asked a few questions during setup, and it’ll do the rest of the work for you.

![](https://cdn-images-1.medium.com/max/4000/0*1m7fFdSUbbNwygw_)

![](https://cdn-images-1.medium.com/max/3896/0*2ueTSjGKVoz0pZ07)

## Start the app

Run yarn dev from the directory you just created, and visit [http://localhost:3000.](http://HTTP://localhost:3000.) Your app is running, and you should see a screen like the one below. We’re in business.

![](https://cdn-images-1.medium.com/max/2372/1*DnL2TMNZxeqKYzTucMA2zA.png)

## Create a user account

Bison comes pre-packaged with a simple user authentication mechanism, so signing in or signing up is a cinch.

First, tap **Login** to get to the login screen, then click **Sign Up**.

![](https://cdn-images-1.medium.com/max/4000/0*RVltyY4DYKY_qtJU)

![](https://cdn-images-1.medium.com/max/4000/0*hJD0S6UWaz08oM2y)

Fill out the form and submit, and you’ll be logged in and redirected to the home page.

![](https://cdn-images-1.medium.com/max/4000/0*kEB97zbpoRM3zqb5)

## Add some database tables

We’ll need a Posts table and model to create and list posts. Edit schema.prisma which is the source of truth for the data model. Adding a Post model is pretty easy.

![](https://cdn-images-1.medium.com/max/3844/0*OOBstCYp9pSvcITr)

Then run yarn prisma migrate save --experimental, to generate a database migration for it.

![](https://cdn-images-1.medium.com/max/4000/0*DQoCjzyEIXXjq_ov)

![](https://cdn-images-1.medium.com/max/3584/0*zakZ33g08S9oAzdC)

Now run the migration using yarn db:migrate .

![](https://cdn-images-1.medium.com/max/4000/0*eFf9TQJa8naPsjlG)

If you look at your database using Postico or psql, you’ll see the resulting table and columns.

![](https://cdn-images-1.medium.com/max/4000/0*rDBGguNV-LE5nb50)

## Generate a page for creating posts

Use Bison’s page generator to stub the /posts/new page:

yarn g:page posts/new

![](https://cdn-images-1.medium.com/max/3200/0*H0d3l7gSehh5vMFF)

## Generate components

We’ll need 2 React components for creating and showing posts PostList and PostForm, so we’ll generate them with Bison’s CLI commands. These wrap 👍some pre-packaged [Hygen](https://www.hygen.io/) generators that Bison comes with.

    yarn g:component PostList
    yarn g:component PostForm

![](https://cdn-images-1.medium.com/max/3208/0*V8LnCOEmjNgTxfiT)

![](https://cdn-images-1.medium.com/max/2564/0*7H4DS9MRxvTP_fAP)

Now you’ll see these 4 new files in your folder tree:

![](https://cdn-images-1.medium.com/max/2000/0*gezBi2ZiFT3fDLVr)

![](https://cdn-images-1.medium.com/max/2000/0*1ZA9-oGrr4ISGowz)

All rightie! Let’s add some code.

## Create the “New Post” form

The page container for /posts/new.tsx is quite simple; aside from styling, we add the <PostForm /> component which we’ll build immediately after.

```react
    import React from 'react';
    import Head from 'next/head';
    import { Heading, Flex } from '@chakra-ui/core';
    
    import { PostForm } from '../../components/PostForm';
    
    function PostsNewPage() {
      return (
        <>
          <Head>
            <title>PostsNewPage</title>
          </Head>
    
          <Flex direction="column" justify="center">
            <Heading size="lg">New Post</Heading>
    
            <PostForm />
          </Flex>
        </>
      );
    }
    
    export default PostsNewPage;
```

### Create the <PostForm /> component

Full code is [here](https://github.com/mcavaliere/bison-blog/blob/master/components/PostForm.tsx). Here are some highlights.

Bison comes packaged with[react-hook-form](https://react-hook-form.com/), so we build the form out like this:

```react
    <form onSubmit={handleSubmit(onSubmit)}>
          <FormControl isInvalid={errors.title}>
            <FormLabel htmlFor="title">Post Title</FormLabel>
            <Input type="text" name="title" id="title" ref={register({ required: true })} />
            <FormErrorMessage>{errors.title && <span>This field is required</span>}</FormErrorMessage>
          </FormControl>

          <FormControl isInvalid={errors.body}>
            <FormLabel htmlFor="body">Post Body</FormLabel>
            <Textarea name="body" id="body" ref={register({ required: true })} />
            <FormErrorMessage>{errors.body && <span>This field is required</span>}</FormErrorMessage>
          </FormControl>

          <Button type="submit">Submit</Button>
        </form>
```
Because Bison also sets up [nexus-plugin-prisma](https://github.com/graphql-nexus/nexus-plugin-prisma)for us, We add a mutation to create a post like so:

```react
    export const CREATE_POST_MUTATION = gql`
      mutation CreatePost($data: PostCreateInput!) {
        createOnePost(data: $data) {
          id
          title
          body
        }
      }
    `;
```
In turn, Bison’s graphql-codegen configuration sees the above and generates a nice React hook for us to plug into 🎉:
```react
    import { useCreatePostMutation } from '../types';
```
We use this and a few other hooks to get our data into the form component:
```react
    export function PostForm() {
      const { register, handleSubmit, errors } = useForm();
      const [createPost] = useCreatePostMutation();
      const router = useRouter();
    
      const { user: { id: userId } = {} } = useAuth();
      // ...
    }
```
And here’s the onSubmit handler which triggers the createPost mutation and redirects back to the homepage upon success:
```react
    const onSubmit = async (data) => {
        // Create the post
        await createPost({
          variables: {
            data: {
              ...data,
              author: {
                connect: {
                  id: userId,
                },
              },
            },
          },
        });
    
        // Redirect to homepage
        await router.replace('/');
      };
```
Now we’ve got a form that saves a post to the database. Voila. 🚀

![](https://cdn-images-1.medium.com/max/2836/0*qhLE_WOvo8Wykj5I)

### Create the <PostList /> component

Now that we’ve got data, let’s display it.

If you remember, we already ran the generator for the <PostList /> component. So we start by importing the <PostList /> into the homepage and displaying it like so:

![](https://cdn-images-1.medium.com/max/2358/0*0n4E884GGRHQKsnx)

Now we’ll need to fetch and display the posts. When fetching data with Bison, we recommend something called a “cell.” A cell is a concept [borrowed from Redwood.js](https://redwoodjs.com/docs/cells). It’s a declarative way of wrapping the GraphQL query together with loading, success, and error states.

`yarn g:cell FetchPosts` will create /cells/FetchPosts.tsx as below.

![](https://cdn-images-1.medium.com/max/2892/1*EwVLWJhTHRt5l7uIUyvO_w.png)

Now we’ll fill the cell out with the correct query and some formatting for the rendered posts.

Full code [here](https://github.com/mcavaliere/bison-blog/blob/master/cells/FetchPosts.tsx). Important snips:
```react
    export const POSTS_QUERY = gql`
      query posts {
        posts {
          id
          title
          body
        }
      }
    `;

    export const Success = ({ posts }: PostsQuery) => {
      return (
        <Stack>
          {posts.map((p) => (
            <Box p={5} shadow="md" key={p.id}>
              <Heading>{p.title}</Heading>
              {p.body}
            </Box>
          ))}
        </Stack>
      );
    };
```
Don’t forget to include the cell in the [<PostList /> component](https://github.com/mcavaliere/bison-blog/blob/master/components/PostList.tsx):
```react
    export function PostList() {  return <FetchPostsCell />;}
```
And we’re done. You now have a (very simple and basic but functioning) blog built in Next.js. Add some data and start having fun.

![](https://cdn-images-1.medium.com/max/3062/0*3rWlgxsesevLJ1fv)

### Conclusion

[Bison](https://github.com/echobind/bisonapp) is in its early stages, but is already a useful tool for kickstarting your Next.js project quickly with great stuff like Prisma, Nexus, and Hygen built-in.

If you try out this tutorial, please drop me a line with any feedback or questions. Or comment right here.

## Contributor’s Bio

*Mike Cavaliere is a web development veteran, personal development devotee, and ADHD/brain improvement aficionado who works as a Senior Software Engineer at [Echobind](https://blog.echobind.com/).*

*He also blogs about software at [MikeCavaliere.com](https://mikecavaliere.com/) and brain improvement at [ADHDTechies.com](https://adhdtechies.com/).*

--

_This post originally appeared on the [Echobind blog](https://blog.echobind.com/creating-your-own-jamstack-blog-in-no-time-with-next-js-and-bison-a3cdd507fd61)._