Opinly SDK

Installation

How to install the Opinly SDK

Opinly SDK is currently in beta. Please join our Discord to get help with any issues you encounter or for feature requests.

Check out the Opinly Blog to see the Opinly SDK in action.

Prerequisites

  • A Next.js project that uses app router (pages router is a WIP, please let us know if you want to use it)
  • Tailwind css installed

Before you can use the Opinly SDK, ensure you have an existing Next.js project running on your own domain. This integration does not require a separate CMS or markdown file management.

Install the SDK packages

pnpm add @opinly/backend @opinly/next 
yarn add @opinly/backend @opinly/next 
npm i @opinly/backend @opinly/next 
  • @opinly/backend: A Node-compatible client for fetching blog content and metadata.
  • @opinly/next: React components, hooks, and utilities for integrating with Next.js routing and ISR.

Edit your Tailwind CSS

Tailwind css is used to style the Opinly SDK. Styles will not be applied without Tailwind css.

If you need to install Tailwind the installation guide can be found here.

globals.css

/* globals.css */
@import "tailwindcss";

/* change the relative path depending on your project structure */
/* STYLING WILL NOT WORK WITHOUT THIS LINE */
@source "../node_modules/@opinly/next";

@theme {
/* --- the rest of your theme --- */

/* These are the colors we use at Opinly, change them to your liking */
/* 
There are no defaults set for the colors below, 
so they will be inherited however you have configured your project if you don't set them
*/


/* Body text */
--color-op-body: #0C0A09;
/* Muted text */
--color-op-muted: #F5F5F4;
/* Heading text */
--color-op-heading: #1e2939;
/* Accent color */
--color-op-accent: #6B7280;
}

/* Or however you do your dark mode */
.dark {
--color-op-body: #0C0A09;
--color-op-muted: #F5F5F4;
--color-op-heading: #1e2939;
--color-op-accent: #6B7280;
}

globals.css


:root {
  /* These are the colors we use at Opinly, change them to your liking. */
  /* There is no default set for these colors, so they will be inherited however you have configured your project */

  /* Body text */
  --color-op-body: #0C0A09;
  /* Muted text */
  --color-op-muted: #F5F5F4;
  /* Heading text */
  --color-op-heading: #1e2939;
  /* Accent color */
  --color-op-accent: #6B7280;
}

/* Or however you do your dark mode */
.dark {
--color-op-body: #0C0A09;
--color-op-muted: #F5F5F4;
--color-op-heading: #1e2939;
--color-op-accent: #6B7280;
}

tailwind.config.js

    module.exports = {
      content: [
        "./pages/**/*.{js,ts,jsx,tsx}",
        "./components/**/*.{js,ts,jsx,tsx}",
        "./node_modules/@opinly/next/**/*.{js,ts,jsx,tsx}",
      ],
      theme: {
        extend: {
          colors: {
            op: {
              body: 'var(--color-op-body)',
              muted: 'var(--color-op-muted)',
              heading: 'var(--color-op-heading)',
              accent: 'var(--color-op-accent)',
            },
          },
        },
      },
    }

Configure your environment variables

Create an environment file (e.g. .env.local) at your project root and add the following variables:

# API key
# Found in Settings > Developers tab
OPINLY_API_KEY="<YOUR_API_KEY>"

Next.js Configuration

Add this to your next.config.js (or next.config.mjs) to proxy image requests through the Opinly CDN:

Make sure to replace the following values with your own:

  • companyName - Retrieve from your dashboard at Settings > Developers
  • cdnNamespace - Retrieve from your dashboard at Settings > Developers
  • siteUrl
import type { NextConfig } from 'next'
import { withOpinlyConfig } from '@opinly/next/config'

const nextConfig: NextConfig = {
  // ... your next config ...
}

const withOpinly = withOpinlyConfig({
  // The path where the blog will be hosted. Make sure it matches the blog route in your app
  blogPath: '/blog',
  // The path where the images will be hosted. Make sure it does not overlap with your own assets
  imagesPath: '/images',
  // The name of the company. This is used in the metadata
  companyName: 'REPLACE ME',
  // The namespace of the CDN. This is used to identify the images in the CDN
  // This is found in the Settings > Developers tab
  cdnNamespace: 'REPLACE-ME-xxxxxx',
  // The URL of the site. This is used in the metadata. Do not include a trailing slash.
  siteUrl: 'https://REPLACE-ME.com',
})

module.exports = withOpinly(nextConfig)

Client Setup

To interact with the Opinly backend, you need to initialize the SDK client. Create a new file at clients/opinly.ts (or .js):

// clients/opinly.ts
import { createOpinlyClient } from '@opinly/backend'

export const opinly = createOpinlyClient({
  fetch: (input, init) => fetch(input, { ...init, cache: 'force-cache' }), // Leverage Next.js ISR caching
})

Parameters:

OptionDescription
apiKeyOptional. Your Opinly API key. Default set from OPINLY_API_KEY env var if not provided.
fetchOptional. Custom fetch wrapper; example uses cache: 'force-cache' for ISR performance.

Make sure to restart your dev server after setting environment variables so process.env is populated.

Blog Integration

Integrate the Opinly SDK into your Next.js application to serve SEO-optimized blog content under your configured prefix (e.g. /blog).

Add a file at app/blog/[[...slug]]/page.tsx:

Make sure that the blog/ route matches the OPINLY_BLOG_PREFIX in your environment variables.

// app/blog/[[...slug]]/page.tsx
import { OpinlyBlog } from '@opinly/next'
import { generateOpinlyMetadata } from '@opinly/next/utils/generate-metadata'
import { opinly } from '@/clients/opinly'
import { ResolvingMetadata } from 'next'

// We set revalidate to false as we'll invalidate the cache via webhooks on the next page
export const revalidate = false

export async function generateMetadata(
  { params }: { params: Promise<{ slug: string[] }> },
  parent: ResolvingMetadata
) {
  return generateOpinlyMetadata(opinly, params, parent)
}

export default async function BlogPage({
  params,
  searchParams,
}: {
  params: Promise<{ slug: string[] }>
  searchParams: Promise<{ page?: string }>
}) {
  const [slugParams, qp] = await Promise.all([params, searchParams])
  const blog = await opinly.content.blog({ params: slugParams })
  return <OpinlyBlog blog={blog} client={opinly} searchParams={qp} />
}

Sitemap Generation

Use the Next.js built-in MetadataRoute API to automatically generate a sitemap.xml that combines your core site routes with all of your Opinly-powered blog posts.

In your app directory, add app/sitemap.ts (or .js) with the following:

import { opinly } from '@/clients/opinly'
import type { MetadataRoute } from 'next'

export const revalidate = false

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  // Fetch records for all blog posts under your configured prefix
  const blogSiteMapRecords = await opinly.content.sitemapRecords({
    blogUrl: `${process.env.OPINLY_SITE_URL}${process.env.OPINLY_BLOG_PREFIX}`,
  })

  return [
    // Spread in all dynamically generated blog entries
    ...blogSiteMapRecords,
  ]
}

Next Steps

Congratulations— youve successfully integrated the Opinly SDK into your Next.js site!

  1. Create blog posts in the Opinly Dashboard under Content.
  2. Schedule your posts to publish automatically on your desired cadence.
  3. Visit /blog to see your blog posts appear.

Once your posts are published, they’ll be indexed by search engines—driving targeted SEO traffic directly to your Next.js application. Happy blogging!