Concepts

Collection and entry data

Collection and entry data concepts for Built.js themes and plugins.

An Entry is a database record in a collection, like a single blog post, an author, or a product. When you create a theme or plugin through the Built Studio, before you can create an entry, you will need to create a content type, which defines the fields for entries.

When you export a theme or plugin and Next.js theme project (see "Creating a new theme"), you can find entry data in public/data/collections/[content-type-plural-name].json.

authors.json
...

Here's an example:

public/data/collections/authors.json
{
  "data": [
    {
      "_id": "author-eb4f3b3c-04ed-4e08-b8fb-6b7db6efcb80",
      "_type": "author",
      "slug": "bob-brown",
      "fullName": "Bob Brown",
      "profile": "profile-fb4f3b3c-04ed-4e08-b8fb-6b7db6efcb60"
    }
  ]
}

In this code, "profile" is a reference to another entry and this can be populated, or "expanded" if you need to access the data in a section template. This is how you can make this "authors" collection data available in a section:

public/data/sections.json
{
  "sections": [
    {
      "name": "team",
      ...
      "collections": {
        "author": {
          "config": { "limit": 3, "populate": [{ "name": "profile" }] }
        }
      }
    }
  ]
}

The "article" property in this code indicates that an article page should be available for this collection (the "authorArticle" is a ModulePage that defines the page and sections needed for the article.

This is how the entries can be used in a React component, "ProfileCards1", which is a template for the "team" section:

components/templates/cards/profile-cards1.tsx
import Image from "next/image";
import Link from "next/link";
import { urlForImage, collectionSlug, entrySlug } from "@/builtjs-utils";
 
export default function ProfileCards1({ content }: any) {
  if (!content) return <></>;
  let { collections = null } = { ...content };
  let data: any = null;
  const DEFAULT_COLS = 3;
  const cols = (data && data.columns) || DEFAULT_COLS;
  if (!collections) {
    throw new Error("No template collections");
  }
  let collectionName = Object.keys(collections)[0];
  let collection = collections[collectionName];
 
  return (
    <section id="profile-cards-1" className="template">
      <div className="max-w-screen-xl mx-auto">
        <div
          className={`grid grid-cols-1 gap-x-6 gap-y-16 lg:grid-cols-${cols}`}
        >
          {collection &&
            collection.slice(0, cols).map((author: any, i: number) => (
              <div key={i}>
                <Link href={`/${collectionSlug(author)}/${entrySlug(author)}`}>
                  <div className="relative w-full h-56 transition-opacity rounded-b-none hover:opacity-80">
                    <Image
                      className="rounded-b-none"
                      src={urlForImage(author?.profile?.profileImage)}
                      fill
                      style={{ objectFit: "cover" }}
                      alt={author.fullName}
                    />
                  </div>
                </Link>
                <div className="p-6 px-8 border border-t-0 rounded-b-lg border-gray-300 dark:border-gray-700">
                  <span className={`preheading blank left`}>
                    {author.position}
                  </span>
                  <Link
                    className="no-underline"
                    href={`/${collectionSlug(author)}/${entrySlug(author)}`}
                  >
                    <h3 className="mb-4 heading-lg hover:text-gray-700 dark:hover:text-gray-300">
                      {author.fullName}
                    </h3>
                  </Link>
                  <p>{author?.profile?.excerpt}</p>
                  <Link
                     className="text-gray-600 dark:text-gray-300"
                    href={`/${collectionSlug(author)}/${entrySlug(author)}`}
                  >
                    Read More
                  </Link>
                </div>
              </div>
            ))}
        </div>
      </div>
    </section>
  );
}

And because the article was specified, the we can have an article page in pages/author/[slug].tsx and the entry will be available to any section templates in the page. In the following code, "ProfileArticle1" is a React component for a section template on the "authorArticle" page.

components/templates/articles/profile-article1.tsx
import Image from "next/image";
import { urlForImage } from "@/builtjs-utils";
import { PortableText } from "@portabletext/react";
 
export default function ProfileArticle1({ content }: any) {
  if (!content) return <></>;
  let { entry: author = null } = { ...content };
 
  let profile = author ? author.profile : null;
  return (
    <article id="profile-article1" className="template">
      {author && (
        <div className="relative max-w-4xl pb-20 mx-auto">
          <header>
            <span className={`preheading blank left`}>{profile?.position}</span>
            <div className="flex items-center">
              <h1 className="mb-10 heading-xxl">{author.fullName}</h1>
            </div>
          </header>
          {profile.profileImage && (
            <div className="relative mb-20">
              <div className="h-96">
                <Image
                  className="object-cover"
                  src={urlForImage(profile?.profileImage)}
                  fill
                  style={{ objectFit: "cover" }}
                  alt={author.fullName}
                />
              </div>
            </div>
          )}
          <PortableText value={profile?.bio} />
        </div>
      )}
    </article>
  );
}

On this page

No Headings