⚠️ Work In ProgressThis template is slightly opinionated based on my main use case for client work, but the core monorepo structure is still a good basis for any Nuxt/Payload project.
I update the repo and packages manually as I continue to refine things during the course of my projects.
If you have any questions/suggestions, feel free to open up an issue.
A Nuxt 3 + TypeScript starter template, with Payload CMS.
# Install packages
pnpm install
# Create .env interactively
pnpm run configure-env
# Update packages interactively (minor versions)
pnpm run update
# Update packages interactively (major versions)
pnpm run update-latest
# Start dev server
pnpm run dev
# Generate gql exports, possibleTypes, and Payload types (run while dev server is active)
pnpm run generate
# Build for production
pnpm run build
# Start server
pnpm run start
Nitro acts as a proxy for all Payload requests during development, using the nitro.devProxy
option in nuxt.config.js
. If you need to proxy additional routes, you can add the to the payloadProxyRoutes
array.
Environment variables are defined in .env
and shared between apps with dotenv-cli
. Variables prefixed with PAYLOAD_PUBLIC_
are exposed to Payload's client-side app.
Running pnpm configure-env
will guide you through creating a .env
file.
Variable | Default | Description |
---|---|---|
NITRO_HOST |
0.0.0.0 |
Host for Nuxt's Nitro server |
NITRO_PORT |
3000 |
Port used by Nitro's server |
PAYLOAD_PUBLIC_SITE_NAME |
Nuxt x Payload Template |
The title of the site (used for page title templates, etc.) |
PAYLOAD_PUBLIC_SITE_URL |
https://localhost:3000 |
URL of the site, including protocol |
PAYLOAD_PUBLIC_PORT |
3001 |
Port used by Payload's Express app |
PAYLOAD_PUBLIC_API_ROUTE |
/_payload |
Route used for Payload's API |
PAYLOAD_PUBLIC_UPLOAD_ROUTE |
/media |
Route used for Payload's upload collections |
DATABASE_NAME |
nuxt-payload |
The name of the MongoDB database |
PAYLOAD_SECRET |
- | A secret key used for encrypting Payload data/sessions |
You will need to set up a custom local hostname for local development, using NGINX and the provided nginx-conf-examples/local.conf
config. If you use mkcert
for local SSL certificates, you can use the provided nginx-conf-examples/local-mkcert.conf
.
Make sure the update the server_name
directive, any changes to the default ports, and the ssl_certificate
and ssl_certificate_key
paths if required.
You will also need our hosts file so the custom hostname resolves to 127.0.0.1
:
# /etc/hosts
127.0.0.1 example.test
💡
hostctl
is a nice CLI tool for managing your hosts file
Payload types can be generated by running the generate
command
pnpm run generate --filter payload
You can easily access Payload's generated types within the Nuxt workspace using the #payload
alias
import type { Image } from '#payload/types'
💡 Type imports must be explicit since Nuxt 3.8, so make sure to use
import type ...
Payload Relationship fields for GraphQL queries are typed as string | MyCollectionType
(or string[] | MyCollectionType[]
when the field uses the hasMany
option). This is because querying the field without any subfields will return the document ID:
# Will return an ID
query GetRelationship {
doc {
relationshipField
}
}
# Will return an object
query GetRelationship {
doc {
relationshipField {
subField
}
}
}
This can cause type issues when using these fields within Nuxt, so there are two composables that can be used to check the type of relationship fields.
<script lang="ts" setup>
const doc = useRelationshipField(data.doc.relationshipField) // RelationshipType | null
const docs = useRelationshipArrayField(data.doc.relationshipHasManyField) // RelationshipType[]
</script>
GraphQL documents are generated from the .gql
files in apps/nuxt/graphql
, and output to apps/nuxt/graphql/index.js
. You can regenerate the exports using the generate
script
pnpm run generate --filter nuxt
💡 Nuxt's generate script will also query the Payload schema, so make sure the Payload app is running (via dev server or otherwise)
The Pages
collection is set up for building predefined page templates, and the usePayloadPage
composable makes it easy to retrieve page data using Apollo, while also automatically set any SEO/Meta values provided by @payloadcms/plugin-seo
.
<script lang="ts" setup>
const doc = await usePayloadPage('Home')
const fields = doc.value?.homeFields
</script>
Globals data is located in the @/stores/globals
store, and is preloaded during SSR (see apps/nuxt/app.vue
).
@sifferhans for telling me about Turborepo and Nitro proxies.