@distromade/distro-cms
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
Related Packages
onebuild
Used to bundle and serve manifests web component that build on any UI frameworks...
10,419
0
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.6.1 | ^3.3.7 | MIT | |
| 0.6.0 | ^3.3.7 | MIT | |
| 0.5.0 | ^3.3.7 | MIT | |
| 0.4.9 | ^3.3.7 | MIT | |
| 0.4.8 | ^3.3.7 | MIT | |
| 0.4.7 | ^3.3.7 | MIT | |
| 0.4.6 | ^3.3.7 | MIT | |
| 0.4.5 | ^3.3.7 | MIT | |
| 0.4.4 | ^3.3.7 | MIT | |
| 0.4.3 | ^3.3.7 | MIT | |
| 0.4.2 | ^3.3.7 | MIT | |
| 0.4.1 | ^3.3.7 | MIT | |
| 0.4.0 | ^3.3.7 | MIT | |
| 0.3.0 | ^3.3.7 | MIT | |
| 0.2.1 | ^3.3.7 | MIT | |
| 0.2.0 | ^3.3.7 | MIT | |
| 0.1.4 | ^3.3.7 | MIT | |
| 0.1.3 | ^3.3.7 | MIT | |
| 0.1.2 | ^3.3.7 | MIT | |
| 0.1.1 | ^3.3.7 | MIT | |
| 0.1.0 | ^3.3.7 | MIT | |
| 0.0.9 | ^3.3.7 | MIT | |
| 0.0.8 | ^3.3.7 | MIT | |
| 0.0.6 | ^3.3.7 | MIT | |
| 0.0.5 | ^3.3.7 | MIT | |
| 0.0.4 | ^3.3.7 | MIT | |
| 0.0.3 | ^3.3.7 | MIT | |
| 0.0.2 | ^3.3.7 | MIT | |
| 0.0.1 | ^3.3.7 | MIT |