Playing with Clerk and Next.js

Playing with Clerk and Next.js

To develop Clubhouse - An open-source learning platform

ยท

5 min read

In order to understand how clerk helps we are going to develop an app on next.js with clerk.

Create a next.js project by executing

 npx create-next-app clubhouse

You can preview your next.js app by executing

npm run dev

Now let's remove all the unnecessary template UI elements. by doing this, the content of your pages/index.js will be:

export default function Home() {
  return (
    <></>
  )
}

Now let's configure Clerk by visiting https://dashboard.clerk.dev/applications

Click on Create application button: image.png

Select standard setup image.png

Enter application name and choose a brand color. image.png

You will see your created project with two environment, production and development. image.png

Select the environment, and scroll down to the instance configuration block image.png

Now we have to include the clerk package in our next.js app, So let's execute the npm command:

npm i @clerk/clerk-react

For styling and icons, let's use material-ui. Install the npm package by executing:

npm install @material-ui/core @material-ui/icons

Create a header component components/Header.js with the following content:

import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import LockOpenIcon from '@material-ui/icons/LockOpen';
import Link from "next/link";
import { SignedIn, SignedOut, UserButton } from "@clerk/clerk-react";

const useStyles = makeStyles((theme) => ({
  grow: {
    flexGrow: 1,
  },
  appBar: {
    background: '#2E3B55'
  }
}));

export default function Header() {
  const classes = useStyles();
  const menuId = 'primary-search-account-menu';

  return (
    <div className={classes.grow}>
      <AppBar position="static" className={classes.appBar}>
        <Toolbar variant="dense">
          <Link href="/" passHref>
          <Typography variant="h6" noWrap>
            Clubhouse
          </Typography>
          </Link>
          <div className={classes.grow} />
          <div>
            <IconButton
              edge="end"
              aria-label="account of current user"
              aria-controls={menuId}
              aria-haspopup="true"
              color="inherit"
            >
              <SignedOut>
                <Link href="/login" title="Login" passHref>
                  <LockOpenIcon/>
                </Link>
              </SignedOut>
              <SignedIn>
                <UserButton />
              </SignedIn>
            </IconButton>
          </div>
        </Toolbar>
      </AppBar>
    </div>
  );
}

It will create an app-bar with app-name and user-avatar/login-icon.

image.png

In this component, import { SignedIn, SignedOut, UserButton } from "@clerk/clerk-react"; includes the conditional rendering child which shows a user avatar or login icon depending upon the user session.

              <SignedOut>
                <Link href="/login" title="Login" passHref>
                  <LockOpenIcon/>
                </Link>
              </SignedOut>

The above implementation is used to show Login-Icon, and on-click of the icon, user will be redirected to the login screen.

              <SignedIn>
                <UserButton />
              </SignedIn>
            </IconButton>

The above implementation is used to show the avatar of the logged-in user. On-click of this avatar, it shows a component with manage account, and logout option as below:

image.png

By default, when a user clicks on the login button, or Manage Account or logout, They always land on the clerk's given account page URLs. See below for your reference: image.png

If you want to keep your users, in your application, and don't want t redirect them to clerk's url, then make the following changes in the Clerk's app setting.

In https://dashboard.clerk.dev/ Go to settings, and then select URL & redirects. Make the following changes, and click on Apply changes. image.png

Let's assume our app is hosted on example.com, then by making the above changes, now when the user clicks on login they will be redirected to example.com/login. When the user clicks on Manage Account they will be redirected to example.com/my-account, and when the user clicks on Signup they will be redirected to example.com/register.

In order to handle the routes in our application, we have to create custom pages for login, my-account and register component.

Create a file pages/login/[[...index]].js with the following contents:

import React from 'react';
import { SignIn } from "@clerk/clerk-react";
import CssBaseline from '@material-ui/core/CssBaseline';
import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';

const useStyles = makeStyles((theme) => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));

export default function Login() {
  const classes = useStyles();

  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <SignIn path="/login" routing="path" />
      </div>
    </Container>
  );
}

In the above snippet, the line <SignIn path="/login" routing="path" /> is responsible for showing the login UI and its controls.

image.png

Create a file pages/my-account/[[...index]].js with the following contents:

import { UserProfile } from "@clerk/clerk-react";
import React from 'react';
import CssBaseline from '@material-ui/core/CssBaseline';
import Header from "../../components/Header";

export default function UserProfilePage() {
  return (
    <>
      <CssBaseline />
      <Header />
      <UserProfile path="/my-account" routing="path" />
    </>
  );
}

The above implementation will show a my-account component. image.png

Create a file pages/register/[[...index]].js with the following contents:

import React from 'react';
import { SignUp } from "@clerk/clerk-react";
import CssBaseline from '@material-ui/core/CssBaseline';
import { makeStyles } from '@material-ui/core/styles';
import Container from '@material-ui/core/Container';

const useStyles = makeStyles((theme) => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  },
}));

export default function Login() {
  const classes = useStyles();

  return (
    <Container component="main" maxWidth="xs">
      <CssBaseline />
      <div className={classes.paper}>
        <SignUp path="/register" routing="path" />
      </div>
    </Container>
  );
}

image.png

Now we are all set to have our own UIs to show based on user session.

In our app, wherever a guest content is to be shown, can be wrapped with <SignedOut></SignedOut>. And all the components which should be visible only to the session user, can be wrapped in <SignedIn></SignedIn>

Follow clerk's official documentation for more information.

Github repo

https://github.com/jaideepghosh/clubhouse

Demo

https://clerkhouse.netlify.app/

#ClerkDev #ClerkHackathon