Get up and running with Lynkow in under 10 minutes. This guide walks you through installing the SDK, connecting to your site, and fetching your first content.

Prerequisites

Before you begin, make sure you have:

  • Node.js 18+ installed (download)

  • A Lynkow account with at least one site created

  • Your Site ID (found in your Lynkow dashboard under Site Settings)

Installation

Install the Lynkow SDK from npm:

Bash
npm install lynkow

Or with your preferred package manager:

Bash
# yarn
yarn add lynkow

# pnpm
pnpm add lynkow

Environment Setup

Create a .env.local file in the root of your project and add your Site ID:

env
NEXT_PUBLIC_LYNKOW_SITE_ID=your-site-id-here

You can find your Site ID in the Lynkow dashboard by navigating to Settings > General. It is a UUID that looks like a1b2c3d4-e5f6-7890-abcd-ef1234567890.

Create the SDK Client

Create a shared client instance that you will import throughout your application. This avoids re-initializing the client on every request.

Create the file lib/lynkow.ts:

TypeScript
import { createClient } from 'lynkow'

export const lynkow = createClient({
  siteId: process.env.NEXT_PUBLIC_LYNKOW_SITE_ID!,
  fetchOptions: {
    next: { revalidate: 60 },
  },
})

Configuration options:

Option

Type

Default

Description

siteId

string

required

Your Lynkow Site ID

locale

string

undefined

Default locale for all requests (e.g., 'en', 'fr')

fetchOptions

RequestInit

undefined

Options passed to every fetch call (headers, cache, etc.)

The SDK connects to https://api.lynkow.com by default. The revalidate: 60 setting tells Next.js to cache responses and revalidate them every 60 seconds via ISR. For near-instant updates, add a webhook endpoint (see Guide 11: Webhooks & Cache Revalidation).

First API Call: List Articles

Create a page that fetches and displays your published articles.

Create the file app/blog/page.tsx:

tsx
import { lynkow } from '@/lib/lynkow'

export default async function BlogPage() {
  const { data: articles, meta } = await lynkow.contents.list({
    limit: 10,
    sort: 'published_at',
    order: 'desc',
  })

  return (
    <main style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
      <h1>Blog</h1>
      <p>{meta.total} articles published</p>

      <ul style={{ listStyle: 'none', padding: 0 }}>
        {articles.map((article) => (
          <li key={article.id} style={{ marginBottom: '2rem' }}>
            <a href={`/blog/${article.slug}`}>
              <h2>{article.title}</h2>
            </a>
            {article.excerpt && <p>{article.excerpt}</p>}
            <time dateTime={article.publishedAt}>
              {new Date(article.publishedAt).toLocaleDateString()}
            </time>
          </li>
        ))}
      </ul>

      <p>
        Page {meta.currentPage} of {meta.lastPage}
      </p>
    </main>
  )
}

The contents.list() method returns published articles with pagination metadata. Each article in the list includes its id, title, slug, path, excerpt, featuredImage, publishedAt, and locale -- but not the full body HTML, which keeps list responses fast.

Display a Single Article

Create a dynamic route to display a full article by its slug.

Create the file app/blog/[slug]/page.tsx:

tsx
import { lynkow } from '@/lib/lynkow'
import { notFound } from 'next/navigation'

type Params = Promise<{ slug: string }>

export default async function ArticlePage({ params }: { params: Params }) {
  const { slug } = await params

  let article
  try {
    article = await lynkow.contents.getBySlug(slug)
  } catch {
    notFound()
  }

  return (
    <main style={{ maxWidth: '800px', margin: '0 auto', padding: '2rem' }}>
      <article>
        <h1>{article.title}</h1>

        {article.author && (
          <p>
            By {article.author.fullName} &middot;{' '}
            <time dateTime={article.publishedAt}>
              {new Date(article.publishedAt).toLocaleDateString()}
            </time>
          </p>
        )}

        {article.featuredImage && (
          <img
            src={article.featuredImageVariants?.hero || article.featuredImage}
            alt={article.title}
            style={{ width: '100%', height: 'auto', borderRadius: '8px' }}
          />
        )}

        <div dangerouslySetInnerHTML={{ __html: article.body }} />

        {article.categories.length > 0 && (
          <div>
            <strong>Categories:</strong>{' '}
            {article.categories.map((cat) => cat.name).join(', ')}
          </div>
        )}

        {article.tags.length > 0 && (
          <div>
            <strong>Tags:</strong>{' '}
            {article.tags.map((tag) => tag.name).join(', ')}
          </div>
        )}
      </article>
    </main>
  )
}

The contents.getBySlug() method returns the full content object including the rendered HTML body, author, categories, tags, structuredData, and SEO fields like metaTitle and metaDescription.

Understanding the Response Types

ContentSummary (returned by contents.list())

Lightweight representation used in listing pages:

TypeScript
interface ContentSummary {
  id: string
  title: string
  slug: string
  path: string
  excerpt: string | null
  featuredImage: string | null
  featuredImageVariants: Record<string, string> | null
  publishedAt: string
  locale: string
  createdAt: string
}

Content (returned by contents.getBySlug())

Full content with body and relationships:

TypeScript
interface Content {
  id: string
  title: string
  slug: string
  path: string
  body: string                    // Rendered HTML
  excerpt: string | null
  featuredImage: string | null
  featuredImageVariants: Record<string, string> | null
  publishedAt: string
  author: {
    id: number
    fullName: string
    avatarUrl: string | null
  } | null
  categories: Array<{
    id: string
    name: string
    slug: string
    path: string
    locale: string
  }>
  tags: Array<{
    id: string
    name: string
    slug: string
    locale: string
  }>
  structuredData: Record<string, unknown> | null
  metaTitle: string | null
  metaDescription: string | null
  ogImage: string | null
  createdAt: string
  updatedAt: string
}

PaginationMeta

Returned as meta in paginated responses:

TypeScript
interface PaginationMeta {
  total: number
  perPage: number
  currentPage: number
  lastPage: number
}

Next Steps

You now have a working integration with Lynkow. Here is where to go from here:

  • Build a Complete Blog with Next.js -- Full blog implementation with categories, tags, pagination, SEO metadata, JSON-LD, and responsive images.

  • API Reference -- Complete documentation of all SDK methods and response types.

  • Media & Images -- Learn how to use lynkow.media.srcset() and lynkow.media.transform() for responsive, optimized images.

  • Localization -- Serve content in multiple languages with locale-aware queries.