Next.js 5-minute Quickstart
This guide will get you all set up to build real-time experiences in Next.js. It takes about 5 minutes. You can start from scratch, or follow along with an existing Next.js project.
S12G is free to use with no sign ups required, so no need to look around for how to create an account to get an API key, it's all ready to go for you below :)
(Optional) Create a new Next.js project
Here's how to create a new Next.js project from scratch. If you already have a Next.js project, you can skip this step. For your convenience it's reproduced here from the Official Next.js Installation Guide.
npx create-next-app@latest
On installation, you'll see the following prompts:
1. What is your project named? s12g-nextjs-example
2. Would you like to use TypeScript? Yes
3. Would you like to use ESLint? No
4. Would you like to use Tailwind CSS? Yes
5. Would you like to use `src/` directory? Yes
6. Would you like to use App Router? (recommended) Yes
7. Would you like to customize the default import alias (@/*)? No
After the prompts, create-next-app
will create a folder with your project name and install the required dependencies. Once it's done, you can cd
into the project folder and setup S12G
.
cd s12g-nextjs-example
Install S12G dependencies
Adding S12G to your project is as simple as installing the @s12g/pusher
package from npm. We're using @harelpls/use-pusher
to make it easy to use S12G with React hooks, but you can use Pusher's own pusher-js
package if you prefer.
yarn add @s12g/pusher @harelpls/use-pusher
Add S12G keys to your project
You'll need to make your S12G keys available in your Next.js environment. S12G keys are just plain ECDSA keys, so you can generate them yourself or use the randomly generated ones below.
Randomly Generated S12G ECDSA Keys
NEXT_PUBLIC_S12G_KEY=Generating...
S12G_SECRET=Generating...
Setup the S12G client provider
Next you'll create a new file at /src/components/s12g-provider.tsx
with your PusherProvider
component. This will make the S12G client available to your React components via the usePusher
hook.
/src/components/s12g-provider.tsx
'use client'
import { PusherProvider, type PusherProviderProps } from '@harelpls/use-pusher'
import React from 'react'
const config: PusherProviderProps = {
// required for private/presence channels
authEndpoint: '/s12g/auth',
// required config props
clientKey: process.env.NEXT_PUBLIC_S12G_KEY,
cluster: 'us', // anything goes here to make typescript happy
// required to use S12G with Pusher's JS client
wsHost: 'stream.s12g.net',
}
export const S12GProvider = ({
children,
...configProp
}: React.PropsWithChildren<Partial<PusherProviderProps>>) => (
<PusherProvider {...{ ...config, ...configProp }}>{children}</PusherProvider>
)
Trigger your first event
Now you're ready to create a demo page to test out S12G. Create a new file at /src/app/demo/page.tsx
and add the following code:
/src/app/demo/page.tsx
'use client'
import { useChannel, useEvent } from '@harelpls/use-pusher'
import { useState } from 'react'
import { S12GProvider } from '@/components/s12g-provider'
type DemoMessageEvent = { message: string }
const Page = () => {
const [messages, setMessages] = useState<DemoMessageEvent[]>([])
const channel = useChannel('quickstart') // don't change 'quickstart' yet
useEvent<DemoMessageEvent>(channel, 'test', message => { // don't change 'test' yet
if (message) setMessages(messages => [...messages, message])
})
return (
<main className="flex min-h-screen flex-col justify-between p-24">
<h1 className="text-xl">S12G Demo</h1>
<div className="mb-auto flex flex-col">
{messages.map((message, i) => (
<pre key={i}>
{i + 1}. {JSON.stringify(message)}
</pre>
))}
</div>
</main>
)
}
export default () => (
<S12GProvider>
<Page />
</S12GProvider>
)
That was fast! Now you're ready to go real-time!
Start your Next.js dev server npm run dev
and open http://localhost:3000/demo in another browser tab. You should see a blank page with a title that says "S12G Demo".
Click this button to trigger a test event and see it appear in your demo page:
Setup authentication for private channels
By convention anyone with your S12G key can subscribe to any public channel. To make a channel private, you must prefix the channel name with private-
eg. private-quickstart
. Private channels are authenticated using a server-side endpoint that signs the channel name and socket ID with your S12G secret key. Let's set that up now.
Create a new file at /src/app/s12g/auth/route.ts
and add the following code:
/src/app/s12g/auth/route.ts
import Pusher from '@s12g/pusher'
import { type NextRequest } from 'next/server'
import process from 'node:process'
const client = new Pusher({
appId: process.env.NEXT_PUBLIC_S12G_KEY!,
key: process.env.NEXT_PUBLIC_S12G_KEY!,
secret: process.env.S12G_SECRET!,
})
export const dynamic = 'force-dynamic'
export async function POST(request: NextRequest) {
const data = await request.formData()
const socketId = data.get('socket_id')
const channelName = data.get('channel_name')
if (!socketId || !channelName)
return new Response('Missing socket_id or channel_name', { status: 400 })
return Response.json(client.authorizeChannel(socketId as string, channelName as string))
}
Now make a tweak to your demo file at /src/app/demo/page.tsx
to subscribe to a private channel:
/src/app/demo/page.tsx
...
const [messages, setMessages] = useState<DemoMessageEvent[]>([])
// change 'quickstart' to 'private-quickstart'
const channel = useChannel('private-quickstart')
useEvent<DemoMessageEvent>(channel, 'test', message => {
...
Click this button to trigger a private test event and see it appear in your demo page:
(Optional) Connect your authentication system
If you have an existing authentication system, you can use it to control access to S12G private channels. You need to make changes to /src/components/s12g-provider.tsx
and /src/app/s12g/auth/route.ts
.
/src/components/s12g-provider.tsx
...
export const S12GProvider = ({
children,
...configProp
}: React.PropsWithChildren<Partial<PusherProviderProps>>) => {
// retrieve your user's secret token from your existing auth system
const Authorization = useMemo(() => `Bearer USER_SECRET_TOKEN_123`, [])
return (
<PusherProvider
{...{
...config,
auth: { // add an `auth` object with `headers` and include `Authorization`
headers: {
Authorization, // here's the value from your auth system
},
},
...configProp, // recommend `configProp` come last in case you want to override `auth`
}}
>
{children}
</PusherProvider>
)
}
...
/src/app/s12g/auth/route.ts
...
if (!socketId || !channelName)
return new Response('Missing socket_id or channel_name', { status: 400 })
// grab the Authorization header from NextRequest
const Authorization = request.headers.get('Authorization')
if (!Authorization) return new Response('Missing Authorization header', { status: 400 })
const [_type, token] = Authorization.split(' ')
// here's where you use the token to authenticate the user with your existing auth system
// we'll just do something silly to demonstrate
if (token !== 'USER_SECRET_TOKEN_123')
// you may want to do more validation here, like determining if the user should have access to `channelName`
return new Response('Unauthorized', { status: 401 })
return Response.json(client.authorizeChannel(socketId as string, channelName as string))
}
Trigger events from your backend
Lastly, you might be wondering how you can trigger events from your backend when you want to send some real-time data to your users, eg when a Stripe webhook arrives.
Let's add an API route to your Next.js project that triggers a test event. Create a new file at /src/app/trigger/route.ts
and add the following code:
/src/app/trigger/route.ts
import Pusher from '@s12g/pusher'
import { type NextRequest } from 'next/server'
import process from 'node:process'
const client = new Pusher({
appId: process.env.NEXT_PUBLIC_S12G_KEY!,
key: process.env.NEXT_PUBLIC_S12G_KEY!,
secret: process.env.S12G_SECRET!,
})
export const dynamic = 'force-dynamic'
export async function GET(_request: NextRequest) {
await client.trigger('private-quickstart', 'test', {
message: `Hello from Next.js at ${new Date().toISOString()}`,
})
return new Response('Event triggered')
}
Now open http://localhost:3000/trigger in another browser tab. You should see "Event triggered". Finally, return to your demo tab and you should see a new message "Hello from Next.js..."