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 layoutconst 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:
- Identifies the content type (e.g., card, group, route)
- Looks for an associated layout
- 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 layoutsconst 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:
- Statically - By changing the layout property in the content definition
- Dynamically - By using conditions to select different layouts based on context
- Programmatically - By updating the layout through code based on user interactions
Example: Dynamic Layout Selection
// Conditional layout based on screen sizeconst 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:
- Experiment quickly - Try different visual presentations without duplicating content
- A/B test - Compare different layouts to see which performs better
- Iterate rapidly - Make incremental improvements to layouts without content changes
- Collaborate effectively - Designers can work on layouts while content creators work on content
During prototyping, create multiple layout variations and use conditions to switch between them. This allows stakeholders to see different options and provide feedback without requiring code changes.
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 overrides are applied at runtime and don’t modify the original content definition. This means you can safely experiment with different layouts without affecting the content stored in your CMS.
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 cardconst buttonCard = { _type: 'vyuh.card', title: 'Click Me', layout: { _type: 'vyuh.card.layout.button', },}
// List item layout for the same cardconst 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 groupconst carouselGroup = { _type: 'vyuh.group', title: 'Featured Items', items: [ /* array of items */ ], layout: { _type: 'vyuh.group.layout.carousel', },}
// Grid layout for the same groupconst 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 routeconst standardRoute = { _type: 'vyuh.route', title: 'Home Page', regions: [ /* regions */ ], layout: { _type: 'vyuh.route.layout.default', },}
// Single item layout for the same routeconst 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 descriptionvyuh.card.layout.button
: Card styled as a button for actionsvyuh.card.layout.listItem
: Compact layout for use in lists
Group Layouts
vyuh.group.layout.carousel
: Horizontal scrolling items with navigation controlsvyuh.group.layout.grid
: Responsive grid layout with configurable columnsvyuh.group.layout.list
: Vertical list of items
Route Layouts
vyuh.route.layout.default
: Standard page layoutvyuh.route.layout.singleItem
: Focused layout for a single content itemvyuh.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 layoutexport 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 definitionexport 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 fileimport { defineType, defineField } from 'sanity'
// Define the schema for your custom layoutexport 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 fileimport { 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:
- Your layout schema is available in Sanity Studio
- Content creators can select your layout when creating or editing content
- The layout configuration is stored in the CMS
- Your React application can render the content with the selected layout
Best Practices
When working with layouts in Vyuh, consider these best practices:
- Keep layouts focused - Each layout should have a clear, specific purpose
- Use semantic names - Name layouts based on their function, not their appearance
- Provide defaults - Always include sensible default values for layout properties
- Consider responsiveness - Design layouts to work well across different screen sizes
- Maintain consistency - Use similar layouts for similar content types
- Document layout options - Make it clear what properties each layout supports
- Test across devices - Verify that layouts work well on all target devices