Twitter Followers Tracker using Next.js, NextAuth and TailwindCSS

Steps to build Twitter followers counter using Next.js, NextAuth, SWR, Tailwind CSS with Dark Mode Support.

To learn new things, just reading the docs is not enough. We should practically apply it. Likewise, while learning new tech stacks we should apply them by developing simple apps to get a hands-on experience. 

So to learn some new tech stacks let build a small application. Here we are going to learn Next.js, NextAuth, SWR and Tailwind CSS by developing a Twitter followers counter app. 

We are going to build an app like TwiterStats.

Tech Stack

  1. Next.js for building ReactJS Application.
  2. NextAuth for OAuth implementation with Twitter.
  3. SWR for fetching data from API.
  4. Tailwind for UI
  5. Twitter Lite for fetching data from Twitter APIs.

Next.js and Tailwind Setup

We can setup tailwind with next.js using a single command, as shown below:

npx create-next-app -e with-tailwindcss twitter-count

The above command automatically configures the Tailwind setup based on the official Next.js example.

Once the installation is completed navigate to your project folder using cd twitter-count and start the dev server using yarn dev command. You can see the below page if you hit http://localhost:3000 in the browser.

Configure NextAuth.js

What is NextAuth?

NextAuth is an open-source Authentication package for Next.js. NextAuth simplifies the social auth logins like Twitter, Google, Apple, Github and many more. It also supports email or passwordless login and database integration.

Add next auth to your project using the below command

yarn add next-auth

Next, create a file named […nextauth].js in pages/api/auth folder and add the below code

import NextAuth from "next-auth"
import TwitterProvider from "next-auth/providers/twitter"
import {cloneDeep} from "tailwindcss/lib/util/cloneDeep";
export default NextAuth({
// Configure one or more authentication providers
providers: [
TwitterProvider({
clientId: process.env.TWITTER_ID,
clientSecret: process.env.TWITTER_SECRET
}),
// ...add more providers here
],
callbacks: {
async jwt({token, user, account, profile, isNewUser}) {
if (profile) {
token['userProfile'] = {
followersCount: profile.followers_count,
twitterHandle: profile.screen_name,
userID: profile.id
};
}
if (account) {
token['credentials'] = {
authToken: account.oauth_token,
authSecret: account.oauth_token_secret,
}
}
return token
},
async session({session, token, user}) {
// Send properties to the client, like an access_token from a provider.
let userData = cloneDeep(token.userProfile);
delete userData.userID;
session.twitter = userData;
return session;
}
},
secret: process.env.NEXTAUTH_SECRET,
pages: {
error: '/error', // Error code passed in query string as ?error=
}
})
view raw nextauth.jsx hosted with ❤ by GitHub

Let's break down the above code

Above NextAuth function handles the dynamic route for all social auth. Here we are going to use Twitter OAuth, so we have added TwitterProvider in providers. To perform successful OAuth we require TWITTER_ID and TWITTER_SECRET, Get these from the Twitter Developer Platform with a few simple steps. 

Follow the steps in this link to get Twitter API access.

After getting the Secrets from the developer portal, Update it in the env and add to the provider as above. 

Using callback set the required data in session after successful OAuth with Twitter. On Successful OAuth, we get many details from Twitter, Here we will customize the data that we require and save it in session. 

We should not expose secrets such as authToken and authSecret to the client-side, so we save them in JWT token objects. Then we can access the user credential on the server-side using the getToken helper method.

secret a random string used to hash tokens, sign or encrypt cookies and generate cryptographic keys.

Configure SessionProvider

Warp the SessionProvier context at the top-level component to use useSession hooks to get session data in child components as below

import '../styles/globals.css'
import {SessionProvider} from "next-auth/react"
import {ThemeProvider} from "next-themes";
export default function App({Component, pageProps: {session, ...pageProps},}) {
return (
<SessionProvider session={session} refetchInterval={5 * 60}>
<ThemeProvider attribute="class" enableSystem={false}>
<Component {...pageProps} />
</ThemeProvider>
</SessionProvider>
)
}
view raw _app.jsx hosted with ❤ by GitHub

refetchInterval is used to fetch the session periodically in the background.

In our app, _app.js is the top-level component, so we have wrapped the session provider. Here we have wrapped ThemeProvide context from next-theme to enable dark mode support. 

Followers Counter Component

Add the below code in Followers Components

import useSWR from 'swr'
import {useState, useEffect} from "react";
import {signOut} from "next-auth/react";
import Image from 'next/image'
import {UserGroupIcon} from "@heroicons/react/solid";
import ToggleButton from "../components/Toggle";
import AnimatePing from "../components/animatePing";
import ProfileImage from "../public/profile-pic.png";
const fetcher = (...args) => fetch(...args).then(res => res.json());
export default function Followers({session}) {
const [isAutoRefresh, setIsAutoRefresh] = useState(false);
const enableAutoRefresh = () => {
setIsAutoRefresh(!isAutoRefresh);
};
const {data, error} = useSWR(session ? '/api/twitter/user' : null, fetcher,
{refreshInterval: isAutoRefresh ? 30000 : 0, revalidateOnFocus: false}
);
if (data && data.error && data.error.errors[0] && data.error.errors[0].code === 89) {
signOut();
}
if (!data) return <div> Loading... </div>;
const userData = data.data;
return (
<figure className="md:flex bg-white rounded-xl p-8 md:p-0 shadow-xl twitter-card dark:bg-gray-800">
<Image
src={session.user.image ? session.user.image : ProfileImage}
alt="Picture of the author"
className="w-24 h-24 md:w-64 md:h-auto rounded-tl-lg rounded-bl-lg mx-auto"
width={500}
height={500}
priority={true}
/>
{isAutoRefresh && <AnimatePing/>}
<div className="pt-6 md:p-4 text-center md:text-left space-y-4">
<div className="flex 2xl:pb-5 flex-col md:flex-row">
<div
className="flex bg-gradient-to-r from-cyan-500 to-blue-500 rounded-lg md:mr-10 p-4 mb-4 md:mb-0
justify-center items-center transition ease-in-out delay-150 hover:-translate-y-1
hover:scale-110 hover:from-pink-500 hover:to-yellow-500">
<UserGroupIcon className="h-10 text-white"/>
<div className="text-4xl ml-4 align-middle font-semibold 2xl:text-6xl">
{userData.followersCount}
</div>
</div>
<div className="flex items-center md:justify-end w-full exclude-in-image justify-center">
<HtmlToImage imageRef={ref} userName={session.user.name}/>
<ToggleButton onChange={() => enableAutoRefresh()} isAutoRefresh={isAutoRefresh}/>
</div>
</div>
{userData &&
<figcaption className="font-medium 2xl:pb-5">
<a className="text-sky-500 2xl:text-4xl" target="_blank"
rel="noreferrer"
href={`https://twitter.com/${userData.twitterHandle}`}>
@{userData.twitterHandle}
</a>
<div className="text-gray-700 2xl:text-3xl text-gray-400">
{userData && userData.location}
</div>
</figcaption>
}
<blockquote>
<p className="text-lg font-medium 2xl:text-3xl dark:text-gray-400">
{userData && userData.description}
</p>
</blockquote>
</div>
</figure>
)
}
view raw Followers.js hosted with ❤ by GitHub

Break Down the Followers Component

What is SWR?

In the followers component, we have called /api/twitter/user API to get basic details of the Twitter user such as name, followers count, profile description and location. We have used SWR to get the data from the API in an interval of time.
 
As the data we get from NextOAuth is not real-time data. So we use Twitter Lite API to get the Twitter user details in real-time. 

Twitter Lite Integration

In Next.js you can build APIs also, files inside api/* are considered as API endpoints. Which are processed on the server, not on the client-side. Twitter APIs can be accessed from the server-side only, so we have a user API in the api/ folder to access the show API using the Twitter lite package.

Add the below code pages/api/twitter/user.js to access the user details using /api/twitter/userAPI.

import {getToken} from 'next-auth/jwt';
import Twitter from "twitter-lite";
export default async (req, res) => {
const token = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET
});
try {
const twitterClient = new Twitter({
consumer_key: process.env.TWITTER_ID,
consumer_secret: process.env.TWITTER_SECRET,
access_token_key: token.credentials.authToken, // from your User (oauth_token)
access_token_secret: token.credentials.authSecret // from your User (oauth_token_secret)
});
//
const userData = await twitterClient.get("users/show", {
id: token.userProfile.userID,
screen_name: token.userProfile.twitterHandle
});
const data = {
twitterHandle: userData.screen_name,
followersCount: userData.followers_count,
description: userData.description,
location: userData.location,
};
return res.status(200).json({
status: 'Ok',
data
});
} catch (error) {
// return error;
return res.status(500).send({ error });
}
}
view raw user.js hosted with ❤ by GitHub

Add the Followers component in index.js file as below.

import {useSession, signIn, signOut} from "next-auth/react"
import {useEffect, useState} from "react";
import Layout from "../components/Layout";
import Button from "../components/Button";
import Followers from "../components/Followers";
import Image from "next/image";
export default function Home() {
const [loader, setLoader] = useState(false);
function oauthSignOut() {
if (!loader) {
setLoader(!loader);
signOut();
}
}
if (status === 'loading') return <Layout> Loading... </Layout>;
function oauthSignIn() {
if (!loader) {
setLoader(!loader);
signIn('twitter');
}
}
if (session) {
return (
<Layout>
<Followers session={session}/>
<div className="flex flex-wrap items-center justify-around max-w-4xl py-6 sm:w-full 2xl:py-12">
<Button label="Logout" onClick={() => oauthSignOut()} loader={loader}>Sign out</Button>
</div>
</Layout>
)
}
return (
<Layout>
<div className="mb-10">
<Image
src="/cover-logo.png"
alt="Picture of the author"
width={800}
height={300}
/>
</div>
<Button label="Login with Twitter" onClick={() => oauthSignIn()} loader={loader}>
Sign in
</Button>
</Layout>
)
}
view raw index.jsx hosted with ❤ by GitHub

Here we use signIn and signOut the method from next-auth to initiate OAuth login. To log in with Twitter we pass Twitter param to the sign-in method as below

signIn('twitter');

Now just hit the URL in the browser to see the changes like below

On calling the signIn method, the app will be redirected to the Twitter OAuth page and clicking the Authorize App button on OAuth Page will redirect back to our followers component as below image.

We need to configure the OAuth redirect URL in Twitter Developer Portal when registering.

You can customize the UI based on your need, Here I have covered only the integration part and with basic UI using tailwind.

Deploying in Vercel

You can deploy your Counter App in Vercel within two steps as below:

  1. Create a Vercel Account
  2. Connect your repository and click deploy.

Links

Conclusion

We have successfully integrated Twitter with NextAuth and displayed the follower's count using Next and tailwind. We have got hands-on experience with these tech stacks now.

Thank you for reading

Get more updates on Twitter.

Free eBook

More Blogs

21