Git CMS

Public read

Show published posts on your public site with the public API, two composables, and one component.

The public site reads published posts through the Public API of core. The composables and the component are for a Nuxt site. A site in any other framework or stack calls the routes. See Use Pluto headless.

A public read is anonymous by design. The answer is the same for every visitor. Drafts never show, not even to a signed-in editor. The response never sets a cookie.

Routes

The post type of the CMS is public: api: { public: true }.

  • GET /api/pluto/v1/content/post returns the published posts: { data, meta: { total, limit, offset } }. A list item has no body. It has excerpt: the excerpt field when it has text, else a plain text version of the body (200 characters).
  • GET /api/pluto/v1/content/post/<slug> returns one published post: { data }. It has the Markdown body and html, the body rendered and sanitized.
  • The list takes limit (1 to 100, default 20), offset, search, sort (for example -publishedAt), and filter[<field>].
  • A draft or a missing file gives 404. A nested type works too: the id can hold slashes.
  • A success answer has Cache-Control: public, s-maxage=60, stale-while-revalidate=300. Set the time with runtimeConfig.plutoApi.maxAge. An error answer has no public cache header.

Your own content types work the same way when they set api: { public: true }. See Public API.

Composables

Both composables wrap useFetch. They run on the server, so the page HTML holds the data.

app/pages/index.vue
<script setup lang="ts">
const { data } = await usePlutoPublicList<{ id: string, title: string, excerpt: string }>('post', { limit: 10 })
</script>

<template>
  <article v-for="post in data?.data" :key="post.id">
    <h2>{{ post.title }}</h2>
    <p>{{ post.excerpt }}</p>
  </article>
</template>
app/pages/posts/[slug].vue
<script setup lang="ts">
const route = useRoute()
const { data } = await usePlutoPublicItem<{ title: string, html: string }>('post', String(route.params.slug))
</script>

<template>
  <PlutoHtml v-if="data" :html="data.data.html" />
</template>
  • usePlutoPublicList(type, { limit, offset, search, sort, filter }) gives the list answer.
  • usePlutoPublicItem(type, slug) gives the item answer, with html. A draft or a missing slug puts a 404 in error.

PlutoHtml

PlutoHtml is a core component. It shows the sanitized html of a public item, from the Git CMS or from any other backend (for example @plutocms/supabase-blog), so a page works with both.

PropDefaultMeaning
htmlnoneThe html of a public item
tagarticleThe wrapper element
classnoneExtra classes for the wrapper

The component sets the HTML as innerHTML. It never parses Markdown on the client. Pass only the html of a public item to it.

PlutoMarkdown is the older name, for the Git CMS only. It renders PlutoHtml.

How the Markdown renders

Pluto renders Markdown with marked and GFM. Raw HTML in the Markdown never reaches the output. Then a sanitizer runs on the result.

The rendered HTML is cached by blob id. A blob id never changes, so the entry never goes stale.

See Security for the sanitizer rules.

Offset your header for the admin bar

On a public page, the admin bar shows for staff only. It is fixed at the top of the page. Core sets the token --pluto-admin-bar-height (2.5rem) on :root. While the bar shows, PlutoRoot sets data-pluto-admin-bar on <html>. When the server renders the bar (see The bar in the first paint), the attribute is in the server HTML too, so the header does not move.

Use them to move a fixed header below the bar:

:root[data-pluto-admin-bar] .site-header {
  top: var(--pluto-admin-bar-height);
}

In script, usePlutoAdminBar().visible tells if the bar shows. See Admin shell.

Show "Edit Post" in the admin bar

The post type shows New Post in the admin bar. To show Edit Post on a post page, tell core which post the page shows:

app/pages/posts/[slug].vue
const route = useRoute()
usePlutoEditLink('post', () => String(route.params.slug))

The function also returns the edit link for staff, and null for a visitor. See New and Edit buttons.