Skip to main content
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. Use SDK server functions inside Next.js Server Components.
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.
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

NEXT_PUBLIC_BOXPRESSD_API_KEY=your_api_key

Server Rendering Events

Events are ideal for server rendering because they contain timely, search-friendly content.
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.
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.
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.
{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.
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.
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:
<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(eventJsonLd),
  }}
/>
Use server-side rendering for pages that benefit from indexable content.
PageSSR RecommendationWhy
HomepageStrongly recommendedCombines events, reviews, sessions, and location content
EventsStrongly recommendedEvent content is search-friendly and time-sensitive
ContactStrongly recommendedLocation, address, hours, and map details matter for local SEO
AboutRecommendedBusiness descriptions and trust content should be indexable
Cigar pagesStrongly recommendedProduct and session content can support AEO
Community pagesRecommendedSessions and check-ins provide fresh content
Admin-only pagesNot neededPrivate 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.
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.