@distromade/distro-cms v0.6.1
Distro CMS
Distro CMS is an open source CMS used to edit Nuxt 3 Content pages, right from your site. Content updates in real-time with 2-way binding.
Usage
1. Create a Nuxt 3 Content project
2. Install Distro CMS and Distro UI
# npm
npm install @distromade/distro-cms @distromade/distro-ui
# yarn
yarn add @distromade/distro-cms @distromade/distro-ui
# pnpm
pnpm add @distromade/distro-cms @distromade/distro-ui
3. Update app.vue
<script setup lang="ts">
import '@distromade/distro-ui/style.css';
import '@distromade/distro-cms/style.css';
import { LoginForm, PageWrapper } from '@distromade/distro-cms';
import type {
LoginFunctionReturnType,
UnsavedChangesType,
} from '@distromade/distro-cms/dist/types';
const contentStore = useContentStore();
const route = useRoute();
function login() {
// Auth logic
}
function save(changes: UnsavedChangesType) {
// Save changes to your project
console.log('Saving changes:', JSON.parse(JSON.stringify(changes)));
}
</script>
<template>
<PageWrapper :content="contentStore.content" :route="route.path" :save="save">
<template #login-form>
<LoginForm :login="login" />
</template>
<NuxtLoadingIndicator color="#222" :height="2" :throttle="400" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</PageWrapper>
</template>
4. How to open the CMS in your project
Add a button or link to open the CMS
<script lang="ts" setup>
import { useGlobalCMSState } from '@distromade/distro-cms';
const { showEditor } = useGlobalCMSState();
function toggleEditor() {
showEditor.value = !showEditor.value;
}
</script>
<template>
<button @click="toggleEditor" />
</template>
5. Add useThemeContent
composable
Under /composables
, create useThemeContent.ts
:
import { computed } from 'vue';
import { v4 as uuid } from 'uuid';
import { useGlobalCMSState } from '@distromade/distro-cms';
import type {
MarkdownElement,
PlainTextElement,
PageContentType,
} from '@distromade/distro-cms/dist/types';
export async function useThemeContent<
M = Record<string, MarkdownElement>,
P = Record<string, PlainTextElement>,
>(contentPath: string) {
const { pageContent } = useGlobalCMSState();
const contentStore = useContentStore();
const content = (await queryContent(contentPath).findOne()) as unknown as PageContentType;
contentStore.setContent(content);
const mdElements = computed(() => {
const { markdownElements } = content.cms;
const { markdownElements: cmsMdElements } = JSON.parse(
JSON.stringify(pageContent.value?.cms ?? {}),
);
if (cmsMdElements) return cmsMdElements as M;
return markdownElements as M;
});
const textElements = computed(() => {
const { plainTextElements } = content.cms;
const { plainTextElements: cmsTextElements } = JSON.parse(
JSON.stringify(pageContent.value?.cms ?? {}),
);
if (cmsTextElements) return cmsTextElements as P;
return plainTextElements as P;
});
const pageTitle = computed<string>(() => {
if (pageContent.value) return pageContent.value.cms.pageTitle;
return content.cms.pageTitle;
});
/**
* This is a hack to force elements bound to mdElements via v-html to re-render
* when the pageContent changes.
*/
const key = computed(() => {
// does nothing; just triggers reactivity:
pageContent.value?.cms;
return uuid();
});
return {
key,
mdElements,
textElements,
pageTitle,
};
}
6. Add useContentStore
in stores
In your /stores
directory, create useContentStore.ts
:
import { defineStore } from 'pinia';
import type { PageContentType } from '@distromade/distro-cms/dist/types';
export interface ContentState {
content: PageContentType | null;
}
export const useContentStore = defineStore('content', {
state(): ContentState {
return {
content: null as PageContentType | null,
};
},
actions: {
setContent(content: PageContentType | null) {
this.content = content;
},
},
});
7. Create .vue page
In your /pages
directory, create a .vue page. In this example, we'll use about.vue for an about page.
<script lang="ts" setup>
import { md, text } from '@distromade/distro-cms';
import json from '~/content/about.json';
import { useThemeContent } from '@/composables/useThemeContent';
type Content = typeof json;
type MD = Content['cms']['markdownElements'];
type Text = Content['cms']['textElements'];
const { mdElements, textElements, pageTitle, key } = await useThemeContent<MD, Text>('/about');
useHead({
title: () => pageTitle.value,
});
</script>
<template>
<section>
<h1>{{ text(textElements.title) }}</h1>
<div v-html="md(mdElements.about)" />
</section>
</template>
You can render either as text in {{ }}
or in v-html
.
8. Create the .json file
In your /content
directory, create about.json
.
{
"contentId": "about",
"cms": {
"pageTitle": "About us",
"pageDescription": "About our company...",
"plainTextElements": {
"title": {
"label": "About title",
"text": "About Killer Coffee Co."
}
},
"markdownElements": {
"about": {
"label": "About description",
"markdown": "This is our company bio, **written in markdown**."
}
}
}
}
9. Run project locally
npm run dev
Now, you can edit your static content with the CMS, right from your project.
Links:
Testing the CMS with Storyboook
To run the Storybook preview locally, while developing, run the following command:
Using NPM
npm install
npm run storybook
Using Yarn
yarn install
yarn storybook
Using PNPM
pnpm install
pnpm storybook
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago