Let Editors Build Custom Pages in Nuxt with Dyrected
Set up Dyrected so your marketing and content team or clients can build new landing pages from approved reusable sections without waiting on a developer for every copy and layout change.
Use this page after Adding a Visual Editor in Nuxt. At this point, the important pieces should already exist:
- the
pagescollection exists - the frontend can render Dyrected content
- preview can open the real route and reflect edits
Now the goal changes. Instead of proving the integration works, you are deciding whether the setup is ready for editors to build real campaign and landing pages without developer help for every change.
By the end, you should know what a safe custom-page workflow looks like, which parts must stay controlled by code, and how the schema, rendering, and preview pieces fit together into one editor-ready system.
Start with the right mental model
For custom landing pages, the goal is usually not "let editors design anything." The goal is "let editors assemble approved page sections fast."
That means the safest setup is:
- the frontend keeps the design system
- the schema defines the approved section types
- editors choose the order and content inside those sections
In Dyrected, that setup is usually:
- a
pagescollection with alayoutblocks field - a Nuxt renderer that maps each
blockTypeto a real component - a preview setup that opens the real page while editors are working
Choose the sections editors should actually control
Before you write any code, list the section types the team needs most often.
A strong first set is usually small:
- hero
- feature grid
- testimonial
- call to action
If a section is not already designed in the frontend, do not add it yet. The fastest dashboard is the one that only offers sections the site already knows how to render well.
Add a layout blocks field to the pages collection
If you have not already done it during the earlier schema steps, this is the key schema change that turns a basic page model into a controlled landing-page builder.
import {
defineBlock,
defineBlocksField,
defineCollection,
defineRelationshipField,
defineRichTextField,
defineTextField,
defineTextareaField,
defineUrlField,
} from "@dyrected/core";
const HeroBlock = defineBlock({
slug: "hero",
labels: { singular: "Hero", plural: "Heroes" },
fields: [
defineTextField({ name: "heading", label: "Heading", required: true }),
defineTextareaField({ name: "subheading", label: "Subheading" }),
defineTextField({ name: "ctaLabel", label: "CTA label" }),
defineUrlField({ name: "ctaUrl", label: "CTA URL" }),
defineRelationshipField({
name: "image",
label: "Image",
relationTo: "media",
}),
],
});
const FeatureGridBlock = defineBlock({
slug: "featureGrid",
labels: { singular: "Feature grid", plural: "Feature grids" },
fields: [
defineTextField({ name: "heading", label: "Heading", required: true }),
defineRichTextField({ name: "intro", label: "Intro" }),
],
});
const TestimonialBlock = defineBlock({
slug: "testimonial",
labels: { singular: "Testimonial", plural: "Testimonials" },
fields: [
defineTextField({ name: "quote", label: "Quote", required: true }),
defineTextField({ name: "name", label: "Name", required: true }),
defineTextField({ name: "role", label: "Role" }),
],
});
const CallToActionBlock = defineBlock({
slug: "callToAction",
labels: { singular: "Call to action", plural: "Calls to action" },
fields: [
defineTextField({ name: "heading", label: "Heading", required: true }),
defineTextareaField({ name: "body", label: "Body" }),
defineTextField({ name: "label", label: "Button label", required: true }),
defineUrlField({ name: "url", label: "Button URL", required: true }),
],
});
export const Pages = defineCollection({
slug: "pages",
labels: { singular: "Page", plural: "Pages" },
admin: {
useAsTitle: "title",
previewUrl: "slug == 'home' ? '/' : '/' + slug",
urlPattern: "/{slug}",
},
fields: [
defineTextField({ name: "title", label: "Title", required: true }),
defineTextField({ name: "slug", label: "Slug", required: true }),
defineBlocksField({
name: "layout",
label: "Page layout",
blocks: [HeroBlock, FeatureGridBlock, TestimonialBlock, CallToActionBlock],
}),
],
});This keeps the page structure flexible, but only inside the set of blocks you approved.
Make sure the frontend can render every approved section
The dashboard is only half of the workflow. Every approved block also needs a matching Nuxt component in the frontend.
That means:
- every
blockTypein the schema must map to a real component - the page route must render the
layoutfield in order - preview must open the same route editors are trying to build
If one of those pieces is missing, this stops being a custom-page workflow and turns back into a schema exercise.
Keep the page builder controlled
This is where the setup either stays useful or becomes messy.
For a quick-start editorial workflow:
- keep global site chrome like navigation and settings outside the page layout
- keep one block focused on one visible section type
- keep field names content-focused, not implementation-focused
- avoid adding arbitrary JSON fields or open-ended style controls
A good page builder helps marketing move fast inside guardrails. It should not ask them to invent layout rules the frontend does not already understand.
Build one real landing page in the dashboard
Once schema, rendering, and preview all exist, create one real landing page in the admin and add a few sections in order:
- a hero
- a feature grid
- a testimonial
- a call to action
Then open that page in preview and make one real editorial change: update the hero copy, reorder a section, or replace a call to action.
That is the moment this guide is aiming for. It proves editors are no longer just editing fields. They are building a real page inside the system you prepared for them.
Know what this page depends on
This workflow only works because the earlier quick-start pages already handled the pieces below:
- Defining a Schema shaped the content model
- Setting Up Initial Data made the first load non-empty
- Displaying Content in Nuxt made the frontend render real content
- Adding a Visual Editor in Nuxt made editors preview and click into that content
This page is where those pieces finally become one editor-ready landing-page workflow.
What this setup gives editors
Once this is in place, the dashboard can support the tasks marketing teams usually care about most:
- create a new landing page
- reorder approved page sections
- update hero copy, proof, and calls to action
- publish a campaign page without asking a developer for every text edit
What it does not do is allow arbitrary new section types or arbitrary design changes. That stays with the product or frontend team, which is usually the safer split.
Where to go deeper
Use this page for the quick-start version of the pattern. For the full background, continue to:
Success check
You are ready for the next step when:
- the
pagescollection has alayoutblocks field - the allowed block types match real designed page sections
- the frontend renders those sections correctly
- preview reflects changes on a real page
- the dashboard can create at least one landing page from those approved sections
Once that is true, your Nuxt quick start is complete. When you are ready to give access to another person, continue to Handing Off to Editors.
Adding a Visual Editor in Nuxt
Add live preview and click-to-edit to a Nuxt route that already renders Dyrected content, starting with the simplest supported preview flow.
Package Reference
Use Dyrected in Nuxt with the first-party module, auto-imported composables, auto-registered components, and an optional self-hosted server handler.