Builder

TsContentPage

Overview

The TsContentPage component is a specialized connector for the page model in Builder.io. It streamlines the integration and rendering of page-specific content, ensuring efficient data handling and seamless dynamic updates tailored for the page model.

Component Code

<template>
  <div :style="{ backgroundColor: `${rootStore.page_background_color}` }">
    <div v-if="content && api_key">
      <Content
        model="page"
        :content="content"
        :api-key="api_key"
        :customComponents="getRegisteredComponents()"
      />
    </div>
    <div
      v-else
      role="status"
      class="flex items-center justify-center h-56 max-w-sm bg-gray-300 rounded-lg animate-pulse dark:bg-gray-700 m-auto my-20"
      style="height: 400px"
    >
      <svg
        class="w-10 h-10 text-gray-200 dark:text-gray-600"
        aria-hidden="true"
        xmlns="http://www.w3.org/2000/svg"
        fill="currentColor"
        viewBox="0 0 16 20"
      >
        <path
          d="M5 5V.13a2.96 2.96 0 0 0-1.293.749L.879 3.707A2.98 2.98 0 0 0 .13 5H5Z"
        />
        <path
          d="M14.066 0H7v5a2 2 0 0 1-2 2H0v11a1.97 1.97 0 0 0 1.934 2h12.132A1.97 1.97 0 0 0 16 18V2a1.97 1.97 0 0 0-1.934-2ZM9 13a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2Zm4 .382a1 1 0 0 1-1.447.894L10 13v-2l1.553-1.276a1 1 0 0 1 1.447.894v2.764Z"
        />
      </svg>
      <span class="sr-only">Loading...</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { fetchOneEntry, Content, isPreviewing } from "@builder.io/sdk-vue";
import { REGISTERED_COMPONENTS } from "~/utils/init-builder";
import { BuilderService } from "../../services/builder.service";
import { datadogLogs } from "@datadog/browser-logs";
import { useI18n } from "vue-i18n";
const { t } = useI18n();

const route = useRoute();

const config = useRuntimeConfig();

const rootStore = useRootStore();

const content = ref();
const page_title = ref("Toolstation");
const api_key = config.public.builderApiKey;

try {
  const route_name_array = route.name.split("___");
  let path = route.path;

  if (route_name_array.length > 1) {
    path = route.path.replace(route_name_array[1] + "/", "");
    path = path.replace(route_name_array[1], "");
  }

  let options = {
    fields: "data",
  };

  async function fetchPageContent(urlPath: string) {
    let userAttributes = {
      urlPath,
    };

    const { data } = await useAsyncData("builderPage", () =>
      new BuilderService(api_key).getItem("page", userAttributes, options)
    );

    if (data.value.data.backgroundColor) {
      rootStore.page_background_color = null;
      rootStore.page_background_color = data.value.data.backgroundColor;
    }

    return data.value;
  }

  content.value = await fetchPageContent(path);

  const capitalizedPageTitle = computed(() => {
    const processTitle = (title: string) => {
      // Replace hyphens with spaces
      return title.replace(/-/g, " ");
    };

    if (content.value?.data?.seoMetaTags?.title) {
      return processTitle(content.value.data.seoMetaTags.title);
    } else if (content.value?.data?.title) {
      return processTitle(content.value.data.title);
    }
    return "Toolstation"; // default title
  });

  if (!content.value) {
    content.value = await fetchPageContent("/404");

    useHead(() => ({
      status: 404,
      title: t("pageNotFound", "Page not found"),
    }));

    throw createError({
      statusCode: 404,
      statusMessage: "404 - Page Not Found",
    });

    /*datadogRum.addError('Page Not Found', {
      error: new Error('Page Not Found'),
      url: route.path,
      statusCode: 404,
    });*/

    datadogLogs.logger.error("404 - Page Not Found", {
      url: route.path,
      referrer: document.referrer, // Add the referral path
      statusCode: 404,
    });
  }

  useHead({
    title: capitalizedPageTitle,
    meta: [
      {
        name: "description",
        content:
          (content.value &&
            content.value.data &&
            content.value.data.seoMetaTags &&
            content.value.data.seoMetaTags.description) ||
          "Beste gereedschap & bouwbenodigdheden voor de professional.",
      },
      {
        name: "keywords",
        content:
          (content.value &&
            content.value.data &&
            content.value.data.seoMetaTags &&
            Array.isArray(content.value.data.seoMetaTags.keywords) &&
            content.value.data.seoMetaTags.keywords.length > 0 &&
            content.value.data.seoMetaTags.keywords.toString()) ||
          "Toolstation",
      },
    ],
  });
} catch (e) {
  useErrorHandler(e);
}

onMounted(() => {
  useCompareStore().clearCompareProducts();
});

const getRegisteredComponents = () => {
  return REGISTERED_COMPONENTS;
};
</script>

Usage

<template>
  <TsContentPage />
</template>

Copyright © 2026