Skip to content

Layouts

Introduction to Layouts

Layouts in the Vyuh framework provide a powerful way to control the visual presentation of content blocks without changing their underlying data. They separate the content structure from its visual representation, enabling flexible and dynamic user interfaces.

Layouts allow you to:

  • Change the appearance of content without modifying its data
  • Reuse the same content with different visual presentations
  • Quickly prototype and experiment with different designs
  • Create responsive interfaces that adapt to different devices and screen sizes
  • Maintain consistency across your application

How Layouts Work

Every content block in Vyuh can have a layout associated with it. The layout defines how the content is visually presented to the user. This separation of concerns follows a key principle:

Content defines what to show, layouts define how to show it.

// Card with a specific layout
const cardWithLayout = {
_type: 'vyuh.card',
title: 'My Card',
description: 'This is a card component',
// The layout property defines how the card is presented
layout: {
_type: 'vyuh.card.layout.button',
// Layout-specific properties
},
}

When a content block is rendered, the Vyuh framework:

  1. Identifies the content type (e.g., card, group, route)
  2. Looks for an associated layout
  3. Renders the content using the specified layout

Layout Selection Mechanism

A content block can have multiple layouts assigned to it, but only the first one is picked for rendering. This “first match” approach simplifies the layout selection process while still providing flexibility.

// Content with multiple layouts
const contentWithLayouts = {
_type: 'vyuh.card',
title: 'My Card',
description: 'This is a card component',
layouts: [
{
_type: 'vyuh.card.layout.button',
// This layout will be used
},
{
_type: 'vyuh.card.layout.default',
// This layout will be ignored
},
],
}

If no layout is explicitly specified, the system falls back to a default layout for the content type. Each content type has a predefined default layout that provides a sensible presentation.

Advantages of the Layout System

The Vyuh layout system offers several key advantages:

1. Separation of Concerns

By separating content from presentation, the layout system enables:

  • Content editors to focus on content without worrying about presentation
  • Designers to iterate on layouts without changing content
  • Developers to implement new layouts without modifying existing content

2. Rapid Prototyping

The ability to quickly switch between layouts makes it easy to:

  • Experiment with different visual designs
  • Test various user experiences
  • Get stakeholder feedback on multiple options
  • Iterate rapidly based on user testing

3. Reusability

The same content can be presented in different ways across your application:

  • Show a card in a compact list view on mobile
  • Display the same card in an expanded grid view on desktop
  • Present the card as a button in a navigation context

4. Consistency

Layouts help maintain visual consistency by:

  • Enforcing design patterns across the application
  • Centralizing styling logic in layout components
  • Providing a library of reusable visual presentations

Switching Between Layouts

One of the most powerful features of the Vyuh layout system is the ability to switch between layouts without changing the underlying content. This can be done:

  1. Statically - By changing the layout property in the content definition
  2. Dynamically - By using conditions to select different layouts based on context
  3. Programmatically - By updating the layout through code based on user interactions

Example: Dynamic Layout Selection

// Conditional layout based on screen size
const responsiveCard = {
_type: 'vyuh.card',
title: 'Responsive Card',
description: 'This card adapts to screen size',
layout: {
_type: 'vyuh.conditional',
condition: {
_type: 'vyuh.condition',
configuration: {
_type: 'vyuh.condition.screenSize',
},
},
cases: [
{
value: 'mobile',
item: {
_type: 'vyuh.card.layout.compact',
},
},
{
value: 'desktop',
item: {
_type: 'vyuh.card.layout.expanded',
},
},
],
defaultCase: 'mobile',
},
}

Prototyping with Layouts

The layout system is particularly valuable during the design and prototyping phase of development. It allows you to:

  1. Experiment quickly - Try different visual presentations without duplicating content
  2. A/B test - Compare different layouts to see which performs better
  3. Iterate rapidly - Make incremental improvements to layouts without content changes
  4. Collaborate effectively - Designers can work on layouts while content creators work on content

Rendering Content with Layouts

Content blocks with layouts are rendered using the content plugin’s render method:

import { useVyuh } from '@vyuh/react-core'
function MyComponent() {
const { plugins } = useVyuh()
// Render content with its defined layout
return plugins.content.render(contentWithLayout)
}

Overriding Layouts at Runtime

One powerful feature of the Vyuh framework is the ability to override layouts at runtime. The render method accepts an options object as its second parameter, which can include an alternative layout:

function MyComponent() {
const { plugins } = useVyuh()
// Override the layout defined in the content
return plugins.content.render(contentObject, {
layout: {
_type: 'vyuh.card.layout.button',
// Layout-specific properties
},
})
}

This is particularly useful for:

  • Testing different layouts without modifying the content definition
  • Creating context-specific presentations of the same content
  • Overriding CMS-defined layouts with application-specific ones
  • Implementing A/B testing of different layouts
  • Creating responsive layouts based on client-side conditions

Layout Application at Different Levels

Layouts can be applied at various levels in the content hierarchy:

Card Layouts

Cards can have different layouts to present the same information in different contexts:

// Button layout for a card
const buttonCard = {
_type: 'vyuh.card',
title: 'Click Me',
layout: {
_type: 'vyuh.card.layout.button',
},
}
// List item layout for the same card
const listItemCard = {
_type: 'vyuh.card',
title: 'Click Me',
layout: {
_type: 'vyuh.card.layout.listItem',
},
}

Group Layouts

Groups can use different layouts to organize their child items:

// Carousel layout for a group
const carouselGroup = {
_type: 'vyuh.group',
title: 'Featured Items',
items: [
/* array of items */
],
layout: {
_type: 'vyuh.group.layout.carousel',
},
}
// Grid layout for the same group
const gridGroup = {
_type: 'vyuh.group',
title: 'Featured Items',
items: [
/* array of items */
],
layout: {
_type: 'vyuh.group.layout.grid',
columns: 3,
},
}

Route Layouts

Even entire routes can have different layouts:

// Default layout for a route
const standardRoute = {
_type: 'vyuh.route',
title: 'Home Page',
regions: [
/* regions */
],
layout: {
_type: 'vyuh.route.layout.default',
},
}
// Single item layout for the same route
const focusedRoute = {
_type: 'vyuh.route',
title: 'Home Page',
regions: [
/* regions */
],
layout: {
_type: 'vyuh.route.layout.singleItem',
},
}

Common Layout Types

The Vyuh framework includes several built-in layout types for different content blocks:

Card Layouts

  • vyuh.card.layout.default: Standard card with image, title, and description
  • vyuh.card.layout.button: Card styled as a button for actions
  • vyuh.card.layout.listItem: Compact layout for use in lists

Group Layouts

  • vyuh.group.layout.carousel: Horizontal scrolling items with navigation controls
  • vyuh.group.layout.grid: Responsive grid layout with configurable columns
  • vyuh.group.layout.list: Vertical list of items

Route Layouts

  • vyuh.route.layout.default: Standard page layout
  • vyuh.route.layout.singleItem: Focused layout for a single content item
  • vyuh.route.layout.tabs: Sectioned content with tab navigation

Creating Custom Layouts

You can extend the Vyuh framework with your own custom layouts. This involves two main steps: creating the layout class and registering it with a content descriptor in your feature.

1. Create the Layout Class

// Custom card layout
export class CustomCardLayout extends LayoutConfiguration<Card> {
// Define the schema type for this layout
static readonly schemaName = 'app.card.layout.custom';
// Define the type descriptor as a static property
static readonly typeDescriptor = new TypeDescriptor(
CustomCardLayout.schemaName,
CustomCardLayout
);
// Custom properties for your layout
readonly backgroundColor?: string;
constructor(data?: Partial<CustomCardLayout>) {
super({
schemaType: CustomCardLayout.schemaName,
title: 'Custom Card Layout'
});
this.backgroundColor = data?.backgroundColor;
}
// Implement the render method to define how the content is displayed
render(content: Card): React.ReactNode {
return (
<div style={{ backgroundColor: this.backgroundColor || '#ffffff' }}>
<h2>{content.title}</h2>
<p>{content.description}</p>
</div>
);
}
}

2. Register the Layout in Your Feature

Layouts are registered through content descriptors in your feature. You simply add the layout’s type descriptor to the appropriate content descriptor:

// Feature definition
export const myFeature = new FeatureDescriptor({
name: 'my-feature',
title: 'My Feature',
description: 'Custom feature with custom layouts',
extensions: [
new ContentExtensionDescriptor({
contents: [
// Register the Card content type with your custom layout
new CardDescriptor({
layouts: [
// Reference the static type descriptor directly
CustomCardLayout.typeDescriptor,
// You can add multiple layouts for the same content type
AnotherCardLayout.typeDescriptor,
],
}),
// Other content descriptors...
],
}),
],
})

3. Register the Layout in Sanity Studio

When using Sanity CMS, you need to register your layout in two places: the React application (as shown above) and in Sanity Studio. This dual registration ensures that content creators can select your layout in the CMS and that your React application can render it correctly.

1. Define the Sanity Schema

// In your schema file
import { defineType, defineField } from 'sanity'
// Define the schema for your custom layout
export const customCardLayoutSchema = defineType({
name: 'app.card.layout.custom', // Must match the schemaName in your layout class
title: 'Custom Card Layout',
type: 'object',
fields: [
defineField({
name: 'backgroundColor',
title: 'Background Color',
type: 'string',
description: 'Background color for the card',
initialValue: '#ffffff',
}),
// Other fields...
],
})

2. Register the Layout in Sanity Feature Descriptor

// In your Sanity feature descriptor file
import { FeatureDescriptor } from '@vyuh/sanity-schema-core'
export const myFeature = new FeatureDescriptor({
name: 'my-feature',
title: 'My Feature',
// Register content types with their layouts
contents: [
// Register the Card content type with your custom layout
new CardDescriptor({
layouts: [
// Include the schema object directly under layouts
customCardLayoutSchema,
],
}),
],
})

This registration process ensures that:

  1. Your layout schema is available in Sanity Studio
  2. Content creators can select your layout when creating or editing content
  3. The layout configuration is stored in the CMS
  4. Your React application can render the content with the selected layout

Best Practices

When working with layouts in Vyuh, consider these best practices:

  1. Keep layouts focused - Each layout should have a clear, specific purpose
  2. Use semantic names - Name layouts based on their function, not their appearance
  3. Provide defaults - Always include sensible default values for layout properties
  4. Consider responsiveness - Design layouts to work well across different screen sizes
  5. Maintain consistency - Use similar layouts for similar content types
  6. Document layout options - Make it clear what properties each layout supports
  7. Test across devices - Verify that layouts work well on all target devices