How to build a table similar to the Prismic Compare plans Slice machine?

Has anyone ever built a table similar the Prismic Compare plan with a slice machine? It looks complicated to build custom types, and I don't know how to achieve it.

Thank you in advance!

Surprise! It’s me again.

I believe for tables, while you CAN do it in Prismic, the “better” solution is to use a service like Airtable. I’ve seen this recommended in other threads.

Thank you for the reply!

I have been learning a lot from you and I appreciate it.
Is AirTable the only solution for this? Can we use both Prismic and AirTable in one project for NextJS?

Did you see this in their FAQ?

Looking at their comparison table that you linked, I have to suspect that this isn't done so that it's editable via Prismic. If I had a client that wanted something like this, I'd create a slice called Table. If the client needed multiple tables, I'd use the variations feature of slices to create the different tables. I'd code the tables as needed. IMHO, this type of data isn't likely to change often enough to need to have it in the Prismic editor.

I totally understand if this doesn't work for your business. Perhaps you deliver a site and then disconnect entirely from the client. This could give you a reason to have them retain you or to contact you again for paid updates. :man_shrugging:

Thank you for the advice! This was very helpful.

1 Like

You can also check out the network tab and download the JSON that the page uses - you can see exactly the model/slice structure that Prismic uses. Hope that's helpful

1 Like

Thank you for the reply!

I don't follow this but I am interested to know more. Do you have a tutorial for this?

Hey!

I built the pricing slice, and indeed it's a bit complex. I'll try to explain. Also, Kris recommendation from above is pretty cool as you can see exactly how it's modelled.

So, for both variations of the pricing slice cards (top of page) and table (the one you refereed to) we have a custom type called "Pricing plan". This custom type consists of fields (a bit simplified) such as:

  • Name
  • Description
  • CTA's
  • Monthly pricing
  • Annual pricing
  • One key text field for each feature, like: Users, API calls, Built-in CDN, CDN Bandwith/Month and so on.

Then, for the cards variation, it's super simple. It just has a content relationship in the repeatable where you can pick which plans to include.

But for the table, it's a bit more advanced. Looking at the provided screenshot, you can see that we have category titles in the static zone, which defined the labels of the grouping. Then in the repeatable zone, we have:

  • A select field called key which contains all keys named the same as all feature fields in the pricing plan custom type.
  • A feature field which is used to name the feature (left column of table).
  • A tooltip where you set the tooltip text. (left column of table)
  • A small text field, for additional text under the feature name (left column of table)

So, for each row you want to create in the table, you add a new repeatable item, you pick which field you want to pull from the pricing plan, you name it and add additional info.

The only thing hard coded is the grouping of features, which you can only name in the table variations static zone explained above.

I hope this make some kind of sense, and feel free to ask follow up questions!

All the best
Samuel

Thank you for the reply!

How do you render in JSX in terms of content in rows and columns (checkbox/without checkbox)?

This is a table row in the desktop version of the table, tell me if it doesn't make sense, in terms of TypeScript it's far from perfect. Havent revisited this component for a while, but you should get an idea of how it renders at least!

const DesktopFeatureRow = ({
  name,
  explanation,
  field,
  full,
  plans,
  borderBottom,
  borderTop,
  smallText,
  theme
}: DesktopFeatureRowProps) => {
  return (
    <tr
      className={clsx(
        "flex text-base lg:text-md font-medium group border-x-2 last:border-b-2 last:rounded-b-xl",
        {
          "text-gray-50 border-gray-15": theme === "light",
          "text-gray-A4 border-gray-50": theme === "dark"
        }
      )}
    >
      <td
        className={clsx(
          "w-[256px] shrink-0 px-6 text-left border-r-2 flex gap-4 justify-between items-center group-last:rounded-bl-[10px]",
          {
            "py-6": full,
            "py-4": !full,
            "border-b-2 group-last:border-b-0": borderBottom,
            "border-t-2": borderTop,
            "bg-gray-F7 border-gray-15": theme === "light",
            "bg-gray-1F border-gray-50": theme === "dark"
          }
        )}
      >
        <span className="text-base">{name}</span>
        {explanation && (
          <div className="relative">
            <Tooltip placement="right">
              <TooltipTrigger>
                <QuestionIcon className="h-6 w-6" />
              </TooltipTrigger>
              <TooltipContent>{explanation}</TooltipContent>
            </Tooltip>
          </div>
        )}
      </td>
      {plans &&
        plans.map(
          (plan, index) =>
            field && (
              <td
                key={index}
                className={clsx(
                  "flex-[1_1_100px] w-0 shrink-0 border-r-2 last:border-r-0 flex justify-center items-center group-last:last:rounded-br-[10px] group-last:last:overflow-hidden",
                  {
                    "border-b-2 group-last:border-b-0": borderBottom,
                    "border-t-2": borderTop,
                    "p-2": smallText,
                    "py-6 px-4": full && !smallText,
                    "py-4 px-4": !full && !smallText,
                    "border-gray-15": theme === "light",
                    "border-gray-50": theme === "dark"
                  }
                )}
              >
                {plan.data[field as keyof Content.PricingPlanDocumentData] ===
                "Check" ? (
                  <>
                    <CheckIcon className="mx-auto text-primary-green h-6 w-6" />
                    <span className="sr-only">Included</span>
                  </>
                ) : (
                  <span
                    className={clsx("text-ellipsis overflow-hidden", {
                      "text-sm font-regular": smallText,
                      "text-base font-bold": !smallText
                    })}
                  >
                    {
                      plan.data[
                        field as keyof Content.PricingPlanDocumentData
                      ] as string
                    }
                  </span>
                )}
              </td>
            )
        )}
    </tr>
  );
};
1 Like

Thank you, Samuel! I will try it out.