nobelium

repository·main·Indexed 25 days ago

https://github.com/craigary/nobelium

A static blog engine built with Next.js that uses Notion as a CMS. Designed for fast, responsive performance and easy deployment on Vercel, nobelium allows users to manage blog content via Notion and configure site metadata, appearance, SEO, analytics, and comments through a blog.config.js file. It supports Docker deployment and provides a set of internal libraries for handling Notion block data, theme management, and RSS feed generation.

Tokens
4.1K
Snippets
6
Records
25
Agent score
70%

What's inside nobelium

  1. Quick Start with Nobelium

    main

    To set up your Nobelium blog, follow these steps:

    1. Duplicate the Notion Template: Use the official Notion template and ensure it is shared to the public.
    2. Fork the Repository: Fork the Nobelium project on GitHub.
    3. Customize Configuration: Edit blog.config.js to match your preferences.
    4. Update Assets (Optional): Replace favicon.svg and favicon.ico in the /public folder with your own.
    5. Deploy to Vercel: Deploy the project to Vercel and configure the required environment variables.
  2. Run Nobelium with Docker

    main

    You can run Nobelium locally or on a server using Docker. There are two methods: building from source or using the default image from GitHub Container Registry.

    Method 1: Build from source

    Use this method if you want to build the image yourself.

    # set env
    export NOTION_PAGE_ID=xxx # your NOTION_PAGE_ID
    export IMAGE=nobelium:latest
    
    # build with docker
    docker build -t ${IMAGE} --build-arg NOTION_PAGE_ID .
    
    # run with docker
    docker run -d --name nobelium -p 3000:3000 -e NOTION_PAGE_ID=${NOTION_PAGE_ID} nobelium:latest

    Method 2: Use default Docker image

    Use this method to pull the pre-built image.

    # pull image
    docker pull ghcr.io/craigary/nobelium:main
    
    # run with docker
    docker run -d --name nobelium -p 3000:3000 -e NOTION_PAGE_ID=${NOTION_PAGE_ID} ghcr.io/craigary/nobelium:main
  3. Configure Nobelium Environment Variables

    main

    When deploying Nobelium (e.g., on Vercel), you must configure the following environment variables:

    VariableRequirementDescription
    NOTION_PAGE_IDRequiredThe ID of the Notion page shared to the web (usually a 32-digit string found in your workspace address).
    NOTION_ACCESS_TOKENOptionalA token used to fetch data if the database is not public. Found in browser cookies as token_v2. Note: This token expires every 180 days and may cause issues with Notion image rendering.

    Note: It is recommended to use the public sharing method rather than providing an access token.

  4. Troubleshoot Nobelium Issues

    main

    Changing Avatars

    Nobelium fetches avatars from Gravatar. To change your avatar, set it on Gravatar using the same email address defined in your blog.config.js.

    Missing Posts after Notion Grouping

    Nobelium does not currently support Notion database grouping. If you use grouping in your Notion database, your posts may disappear. Workaround: Use Notion views with filters instead of grouping to manage your posts.

  5. Configure next-sitemap settings

    main

    The next-sitemap.config.js file defines the configuration for generating sitemaps and robots.txt for the Nobelium project. It uses the config.link value from the internal server configuration as the base siteUrl.

    Available configuration options include:

    • siteUrl: The base URL for the sitemaps (mapped to config.link).
    • generateRobotsTxt: Boolean indicating whether to generate a robots.txt file. Set to true by default.
    • sitemapSize: The maximum number of URLs per sitemap file. Set to 7000 by default.
    • generateIndexSitemap: Boolean indicating whether to generate a sitemap index file. Set to false by default.

    For a full list of supported options, refer to the next-sitemap documentation.

    module.exports = {
      siteUrl: config.link,
      generateRobotsTxt: true,
      sitemapSize: 7000,
      generateIndexSitemap: false
      // ...other options
    }
  6. Configure Comment Providers (gitalk, utterances, or cusdis)

    main

    Set comment.provider to one of the supported providers: 'gitalk', 'utterances', or 'cusdis'.

    • gitalk: Requires repo, owner, clientID, and clientSecret in comment.gitalkConfig.
    • utterances: Requires repo in comment.utterancesConfig.
    • cusdis: Requires appId in comment.cusdisConfig. You can also customize host and scriptSrc if using a self-hosted version.
    comment: {
      provider: 'gitalk', // 'gitalk', 'utterances', or 'cusdis'
      gitalkConfig: {
        repo: '',
        owner: '',
        admin: [],
        clientID: '',
        clientSecret: '',
        distractionFreeMode: false
      },
      utterancesConfig: {
        repo: ''
      },
      cusdisConfig: {
        appId: '',
        host: 'https://cusdis.com',
        scriptSrc: 'https://cusdis.com/js/cusdis.es.js'
      }
    }
  7. Configure Nobelium via blog.config.js

    main
    Nobelium uses a blog.config.js file located in the project's current working directory to define its configuration. This file is evaluated at runtime to provide both server-side config and client-side clientConfig objects. Ensure your blog.config.js exports a configuration object using module.exports.
  8. Configure Analytics (Google Analytics or Ackee)

    main

    To enable analytics, set the analytics.provider field to either 'ga' or 'ackee'.

    • Google Analytics: Provide your ID in analytics.gaConfig.measurementId (e.g., G-XXXXXXXXXX).
    • Ackee: Provide tracker, dataAckeeServer (without a trailing slash), and domainId in analytics.ackeeConfig.
  9. Configure Nobelium via blog.config.js

    main

    The blog.config.js file is the primary configuration object for Nobelium. It controls site metadata, appearance, Notion integration, SEO, analytics, and comments.

    Important Environment Variables:

    • NOTION_PAGE_ID: The ID of your Notion page. DO NOT CHANGE THIS.
    • NOTION_ACCESS_TOKEN: Required if your Notion database is not public.

    Key Configuration Groups:

    • Site Metadata: title, author, email, link, description, lang, timezone, since.
    • Appearance: appearance ('light', 'dark', or 'auto'), font ('sans-serif' or 'serif'), lightBackground, and darkBackground (hex values).
    • Layout: postsPerPage, sortByDate, showAbout, showArchive, autoCollapsedNavBar.
    • SEO & Social: ogImageGenerateURL, socialLink, seo (including keywords and googleSiteVerification).
    • Analytics: Supports ga (Google Analytics) or ackee. Requires specific configuration objects for each.
    • Comments: Supports gitalk, utterances, or cusdis.
    const BLOG = {
      title: 'CRAIGARY',
      author: 'Craig Hart',
      email: 'i@craigary.net',
      link: 'https://nobelium.vercel.app',
      description: 'This gonna be an awesome website.',
      lang: 'en-US',
      timezone: 'Asia/Shanghai',
      appearance: 'auto',
      font: 'sans-serif',
      lightBackground: '#ffffff',
      darkBackground: '#18181B',
      path: '',
      since: 2021,
      postsPerPage: 7,
      sortByDate: false,
      showAbout: true,
      showArchive: true,
      autoCollapsedNavBar: false,
      ogImageGenerateURL: 'https://og-image-craigary.vercel.app',
      socialLink: 'https://twitter.com/craigaryhart',
      seo: {
        keywords: ['Blog', 'Website', 'Notion'],
        googleSiteVerification: ''
      },
      notionPageId: process.env.NOTION_PAGE_ID,
      notionAccessToken: process.env.NOTION_ACCESS_TOKEN,
      analytics: {
        provider: '',
        ackeeConfig: {
          tracker: '',
          dataAckeeServer: '',
          domainId: ''
        },
        gaConfig: {
          measurementId: ''
        }
      },
      comment: {
        provider: '',
        gitalkConfig: {
          repo: '',
          owner: '',
          admin: [],
          clientID: '',
          clientSecret: '',
          distractionFreeMode: false
        },
        utterancesConfig: {
          repo: ''
        },
        cusdisConfig: {
          appId: '',
          host: 'https://cusdis.com',
          scriptSrc: 'https://cusdis.com/js/cusdis.es.js'
        }
      },
      isProd: process.env.VERCEL_ENV === 'production'
    }
    
    module.exports = BLOG
  10. Generate an RSS feed with generateRss()

    main

    The generateRss function creates an Atom 1.0 feed from an array of post objects. It uses the project's global configuration (title, description, link, lang, author, etc.) to populate the feed metadata and renders the full content of each post using the NotionRenderer.

    Each post in the input array must contain the following properties:

    • title: The title of the post.
    • slug: The URL slug used to generate the post's unique ID and link.
    • summary: A short description for the feed item.
    • id: The Notion page ID used to fetch post blocks.
    • date: A date string or object representing the post publication date.

    The function returns an Atom 1.0 formatted string.

  11. Fetch all posts using getAllPosts()

    main

    The getAllPosts function retrieves post data from a Notion database. It requires the NOTION_PAGE_ID environment variable to be set. The function processes the Notion database, extracts properties, converts dates to Unix millisecond timestamps, and applies filtering and sorting based on the project's configuration.

    Parameters:

    • includePages (boolean, default: false): If set to true, the returned array will include pages that do not meet the standard post requirements. If false, only published posts are returned.

    Returns:

    • An array of post objects, or null if the provided NOTION_PAGE_ID does not point to a valid Notion database (collection view or collection view page).
  12. Fetch and paginate posts with getStaticProps

    main

    In a Next.js environment, use getStaticProps to fetch blog posts from Notion using getAllPosts. You can limit the number of posts displayed on the home page by slicing the array based on clientConfig.postsPerPage. The function returns postsToShow, the current page (defaulting to 1), and a showNext boolean indicating if more posts are available. It also includes a revalidate: 1 property for Incremental Static Regeneration (ISR).

    export async function getStaticProps () {
      const posts = await getAllPosts({ includePages: false })
      const postsToShow = posts.slice(0, clientConfig.postsPerPage)
      const totalPosts = posts.length
      const showNext = totalPosts > clientConfig.postsPerPage
      return {
        props: {
          page: 1, // current page is 1
          postsToShow,
          showNext
        },
        revalidate: 1
      }
    }