> ## Documentation Index
> Fetch the complete documentation index at: https://docs.boxpressd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js SSR

> Use server-side rendering with the Boxpressd Sites SDK for better SEO, AEO, and performance.

Server-side rendering is the recommended approach for most Boxpressd Sites projects.

By fetching Boxpressd data on the server, your pages can include events, reviews, sessions, check-ins, location details, and other business content directly in the HTML returned to search engines, AI crawlers, and social platforms.

This helps improve:

* Search engine visibility
* AI answer engine visibility
* Page performance
* Social previews
* Accessibility
* Reliability on slower devices

## Why SSR Matters

Many Boxpressd Sites are marketing websites.

That means the content should be easy for search engines and AI assistants to understand.

When data is fetched only in the browser, important content may not be visible in the initial HTML.

When data is fetched on the server, crawlers can see meaningful content immediately.

For example, a lounge website can render:

* Upcoming events
* Recent reviews
* Location details
* Recent check-ins
* Smoking session activity
* Business hours
* Contact details

directly into the page source.

## Recommended Pattern

Use SDK server functions inside Next.js Server Components.

```tsx theme={null}
import { getBoxpressdEvents } from "@boxpressd/sites-sdk/events"
import { getBoxpressdCheckins } from "@boxpressd/sites-sdk/checkins"
import { getBoxpressdSessions } from "@boxpressd/sites-sdk/sessions"

export default async function HomePage() {
  const [events, checkins, sessions] = await Promise.all([
    getBoxpressdEvents({ status: "upcoming", limit: 3 }),
    getBoxpressdCheckins({ limit: 5 }),
    getBoxpressdSessions({ limit: 5 }),
  ])

  return (
    <main>
      <section>
        <h1>Stogies Cigar Lounge & Tap House</h1>
        <p>
          Premium cigars, craft drinks, and a welcoming lounge experience in
          Chapin, South Carolina.
        </p>
      </section>

      <section>
        <h2>Upcoming Events</h2>
        {events.map((event) => (
          <article key={event.id}>
            <h3>{event.title}</h3>
            <p>{event.description}</p>
          </article>
        ))}
      </section>

      <section>
        <h2>Recent Check-ins</h2>
        {checkins.map((checkin) => (
          <article key={checkin.id}>
            <p>{checkin.user.displayName} checked in.</p>
          </article>
        ))}
      </section>

      <section>
        <h2>Recent Sessions</h2>
        {sessions.map((session) => (
          <article key={session.id}>
            <p>
              {session.user.displayName} smoked {session.cigar?.name}.
            </p>
          </article>
        ))}
      </section>
    </main>
  )
}
```

## Configure the Provider

Even when fetching data on the server, wrap your application with the `BoxpressdProvider` so SDK components, hooks, and theme values are available throughout the site.

```tsx theme={null}
import { BoxpressdProvider } from "@boxpressd/sites-sdk"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <BoxpressdProvider
          apiKey={process.env.NEXT_PUBLIC_BOXPRESSD_API_KEY!}
        >
          {children}
        </BoxpressdProvider>
      </body>
    </html>
  )
}
```

## Environment Variables

<CodeGroup>
  ```env Next.js theme={null}
  NEXT_PUBLIC_BOXPRESSD_API_KEY=your_api_key
  ```

  ```env Local API theme={null}
  NEXT_PUBLIC_BOXPRESSD_API_KEY=your_api_key
  BOXPRESSD_API_BASE_URL=http://localhost:3000
  ```
</CodeGroup>

## Server Rendering Events

Events are ideal for server rendering because they contain timely, search-friendly content.

```tsx theme={null}
import { getBoxpressdEvents } from "@boxpressd/sites-sdk/events"

export default async function EventsPage() {
  const events = await getBoxpressdEvents({
    status: "upcoming",
    limit: 12,
  })

  return (
    <main>
      <h1>Upcoming Cigar Events</h1>

      {events.map((event) => (
        <article key={event.id}>
          <h2>{event.title}</h2>
          <p>{event.description}</p>
          <time dateTime={event.startsAt}>
            {new Date(event.startsAt).toLocaleDateString()}
          </time>
        </article>
      ))}
    </main>
  )
}
```

## Server Rendering Sessions

Sessions can help cigar brands and lounges show real community activity.

```tsx theme={null}
import { getBoxpressdSessions } from "@boxpressd/sites-sdk/sessions"

export default async function CommunityPage() {
  const sessions = await getBoxpressdSessions({
    limit: 10,
  })

  return (
    <main>
      <h1>What People Are Smoking</h1>

      {sessions.map((session) => (
        <article key={session.id}>
          <h2>{session.cigar?.name}</h2>
          <p>
            Smoked by {session.user.displayName}
            {session.venue?.name ? ` at ${session.venue.name}` : ""}
          </p>
        </article>
      ))}
    </main>
  )
}
```

## Server Rendering Cigar-Specific Sessions

Cigar-specific sessions are especially useful for brand and product pages.

```tsx theme={null}
import { getBoxpressdSessions } from "@boxpressd/sites-sdk/sessions"

export default async function CigarPage({
  params,
}: {
  params: {
    cigarId: string
  }
}) {
  const sessions = await getBoxpressdSessions({
    cigarId: params.cigarId,
    limit: 10,
  })

  return (
    <main>
      <h1>Community Sessions</h1>

      {sessions.map((session) => (
        <article key={session.id}>
          <p>{session.user.displayName} rated this cigar {session.rating}</p>
          {session.note && <p>{session.note}</p>}
        </article>
      ))}
    </main>
  )
}
```

## Handling Empty States

Not every business will have events, check-ins, or sessions available.

Server-rendered pages should handle empty states gracefully.

```tsx theme={null}
{events.length > 0 ? (
  <section>
    <h2>Upcoming Events</h2>
    {events.map((event) => (
      <article key={event.id}>{event.title}</article>
    ))}
  </section>
) : null}
```

For marketing pages, it is often better to hide empty sections rather than display empty placeholders.

## Metadata

Use server-fetched data to generate stronger page metadata.

```tsx theme={null}
import type { Metadata } from "next"
import { getBoxpressdEvents } from "@boxpressd/sites-sdk/events"

export async function generateMetadata(): Promise<Metadata> {
  const events = await getBoxpressdEvents({
    status: "upcoming",
    limit: 1,
  })

  const nextEvent = events[0]

  return {
    title: nextEvent
      ? `${nextEvent.title} | Upcoming Cigar Event`
      : "Upcoming Cigar Events",
    description: nextEvent?.description
      ?? "Explore upcoming cigar events, lounge gatherings, and community experiences.",
  }
}
```

## Structured Data

Server-rendered data can also be used to generate JSON-LD.

```tsx theme={null}
const eventJsonLd = events.map((event) => ({
  "@context": "https://schema.org",
  "@type": "Event",
  name: event.title,
  description: event.description,
  startDate: event.startsAt,
  endDate: event.endsAt,
  location: {
    "@type": "Place",
    name: event.locationName,
    address: event.address,
  },
}))
```

Then render it into the page:

```tsx theme={null}
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(eventJsonLd),
  }}
/>
```

## Recommended Page Strategy

Use server-side rendering for pages that benefit from indexable content.

| Page | SSR Recommendation | Why |
| - | - | - |
| Homepage | Strongly recommended | Combines events, reviews, sessions, and location content |
| Events | Strongly recommended | Event content is search-friendly and time-sensitive |
| Contact | Strongly recommended | Location, address, hours, and map details matter for local SEO |
| About | Recommended | Business descriptions and trust content should be indexable |
| Cigar pages | Strongly recommended | Product and session content can support AEO |
| Community pages | Recommended | Sessions and check-ins provide fresh content |
| Admin-only pages | Not needed | Private or interactive pages do not need SEO |

## Client Components Still Have a Place

Use client components for interactive experiences.

Good examples include:

* Event modals
* Filters
* Tabs
* Carousels
* Map interactions
* Add-to-calendar buttons
* Age verification
* Client-side personalization

A common pattern is to fetch data on the server and pass it into a client component.

```tsx theme={null}
import { getBoxpressdEvents } from "@boxpressd/sites-sdk/events"
import { EventsClient } from "./events-client"

export default async function EventsPage() {
  const events = await getBoxpressdEvents({
    status: "upcoming",
    limit: 12,
  })

  return <EventsClient initialEvents={events} />
}
```

## Best Practices

* Fetch important marketing content on the server.
* Use `Promise.all` when fetching independent datasets.
* Hide empty sections on public marketing pages.
* Use SDK components for display and SDK helpers for data.
* Generate metadata from server-fetched content where useful.
* Add JSON-LD for events, locations, and products when appropriate.
* Keep client components focused on interaction.

## Next Steps

Continue to:

* Cigar Lounge Example
* Cigar Brand Example
* Retailer Example
* Stogies Case Study

These examples show how server-rendered Boxpressd data can power complete real-world websites.
