Nuxt 3 component (SSR)

<template>
  <a
    class="inline-flex gap-2 items-center px-3 py-1 text-sm rounded-md border border-border text-white/70 hover:bg-muted hover:text-white max-w-fit"
    :href="statusSiteUrl"
    target="_blank"
    rel="noreferrer"
  >
    <span class="flex relative w-2 h-2">
      <span
        v-if="status.label === 'Operational'"
        class="inline-flex absolute w-full h-full rounded-full opacity-75 duration-1000 animate-ping"
        :class="status.color"
      />
      <span
        class="inline-flex relative w-2 h-2 rounded-full"
        :class="status.color"
      />
    </span>
    {{ status.label }}
  </a>
</template>
<script setup lang="ts">
import * as z from "zod";

const config = useRuntimeConfig();
const statusSiteUrl = config.statusSiteSlug;

const status = ref({
  label: "checking",
  color: undefined,
});

const statusEnum = z.enum([
  "operational",
  "degraded_performance",
  "partial_outage",
  "major_outage",
  "under_maintenance",
  "unknown",
]);

const statusSchema = z.object({ status: statusEnum });

const dictionary = {
  operational: {
    label: "Operational",
    color: "bg-green-500",
  },
  degraded_performance: {
    label: "Degraded Performance",
    color: "bg-yellow-500",
  },
  partial_outage: {
    label: "Partial Outage",
    color: "bg-yellow-500",
  },
  major_outage: {
    label: "Major Outage",
    color: "bg-red-500",
  },
  unknown: {
    label: "Unknown",
    color: "bg-gray-500",
  },
  under_maintenance: {
    label: "Under Maintenance",
    color: "bg-gray-500",
  },
} as const;

const { data } = await useFetch("/api/site-status");
const parsed = statusSchema.safeParse(data);
const key = !parsed.success ? "unknown" : parsed.data.status;
status.value = dictionary[key];
</script>

Nuxt 2 Component (No TS, No SSR)

<template>
  <a
    class="inline-flex gap-2 items-center px-3 py-1 text-sm rounded-md border border-border text-white/70 hover:bg-muted hover:text-white max-w-fit"
    :href="statusSiteUrl"
    target="_blank"
    rel="noreferrer"
  >
    <span class="flex relative w-2 h-2">
      <span
        v-if="status.label === 'Operational'"
        class="inline-flex absolute w-full h-full rounded-full opacity-75 duration-1000 animate-ping"
        :class="status.color"
      />
      <span
        class="inline-flex relative w-2 h-2 rounded-full"
        :class="status.color"
      />
    </span>
    {{ status.label }}
  </a>
</template>
<script>
const statusEnum = [
  "operational",
  "degraded_performance",
  "partial_outage",
  "major_outage",
  "under_maintenance",
  "unknown",
];

const dictionary = {
  operational: {
    label: "Operational",
    color: "bg-green-500",
  },
  degraded_performance: {
    label: "Degraded Performance",
    color: "bg-yellow-500",
  },
  partial_outage: {
    label: "Partial Outage",
    color: "bg-yellow-500",
  },
  major_outage: {
    label: "Major Outage",
    color: "bg-red-500",
  },
  unknown: {
    label: "Unknown",
    color: "bg-gray-500",
  },
  under_maintenance: {
    label: "Under Maintenance",
    color: "bg-gray-500",
  },
};

export default {
  data() {
    return {
      status: {
        label: "Checking",
        color: "",
      },
      statusSiteUrl: "read-from-runtime-config",
    };
  },
  mounted() {
    this.fetchSiteStatus();
  },
  methods: {
    async fetchSiteStatus() {
      const { data } = await $fetch((`/api/site-status`);
      const key = statusEnum.includes(data.status) ? data.status : "unknown";
      this.status = dictionary[key];
    },
  },
};
</script>

Serverless function to fetch status from Openstatus public API

export default defineEventHandler((event) => {
  const config = useRuntimeConfig();

  const resp = await $fetch(
    `https://api.openstatus.dev/public/status/${config.statusSiteSlug}`,
    {
      next: { revalidate: 60 }, // cache request for 60 seconds
    }
  );
  return response.status(200).json(resp.data);
})