Skip to content

Layout

Dill pixel provides two main ways to handle layouts in your game: FlexContainer and UICanvas. These classes offer different approaches to positioning and organizing UI elements, both powered by the robust @pixi/layout system.

The FlexContainer provides a flexible box layout model similar to CSS Flexbox, built on top of @pixi/layout. It’s ideal for creating dynamic layouts that need to adapt to different screen sizes or content.

// Create a flex container
const flexContainer = this.add.flexContainer({
layout: {
gap: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
},
});
// Add items to the container
flexContainer.add.text({ text: 'Item 1', style: textStyle });
flexContainer.add.text({ text: 'Item 2', style: textStyle });
flexContainer.add.text({ text: 'Item 3', style: textStyle });

The FlexContainer supports several configuration options:

const container = this.add.flexContainer({
// Layout properties (passed to @pixi/layout)
layout: {
width: 800,
height: 200,
gap: 20,
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
justifyContent: 'space-between',
},
// Auto-layout children (default: true)
autoLayoutChildren: true,
// Bind to app size for responsive layouts
bindToAppSize: false,
// Bind to another container's size
bindTo: someContainer,
});

The layout property accepts any valid @pixi/layout options:

  • flexDirection: Controls the direction of items (‘row’ | ‘column’ | ‘row-reverse’ | ‘column-reverse’)
  • flexWrap: Determines if items should wrap (‘wrap’ | ‘nowrap’ | ‘wrap-reverse’)
  • alignItems: Aligns items on the cross axis (‘center’ | ‘flex-start’ | ‘flex-end’ | ‘stretch’ | ‘baseline’)
  • justifyContent: Aligns items on the main axis (‘center’ | ‘flex-start’ | ‘flex-end’ | ‘space-between’ | ‘space-around’ | ‘space-evenly’)
  • gap: Space between items (number)
  • width/height: Container dimensions (number | ‘auto’ | ‘intrinsic’ | percentage string)
  • padding: Inner spacing (number | object with top/left/bottom/right)
  • margin: Outer spacing (number | object with top/left/bottom/right)

FlexContainer provides convenient getters and setters for common layout properties:

// Direct property access (updates layout automatically)
container.gap = 15;
container.flexDirection = 'column';
container.alignItems = 'center';
container.justifyContent = 'space-between';
// Size control
container.size = { width: 400, height: 300 };
container.size = [400, 300]; // Array format
container.size = 400; // Square format
// Individual dimensions
container.layoutWidth = 'auto';
container.layoutHeight = 200;

When autoLayoutChildren is true (default), child elements are automatically configured for layout:

const container = this.add.flexContainer({
autoLayoutChildren: true, // default
layout: { flexDirection: 'row', gap: 10 },
});
// Children are automatically given layout properties
const child = container.add.text({ text: 'Auto-layout text' });
// child.layout is automatically set to { isLeaf: true }

Bind containers to app size or other containers for responsive behavior:

// Bind to application size
const responsiveContainer = this.add.flexContainer({
bindToAppSize: true,
layout: {
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
});
// Bind to another container
const childContainer = this.add.flexContainer({
bindTo: parentContainer,
layout: { width: '100%', height: 'auto' },
});

FlexContainers can be nested to create complex layouts:

const mainContainer = this.add.flexContainer({
layout: {
flexDirection: 'column',
gap: 20,
width: '100%',
height: '100%',
},
});
const topRow = mainContainer.add.flexContainer({
layout: {
flexDirection: 'row',
gap: 10,
alignItems: 'center',
width: '100%',
height: 'auto',
},
});
topRow.add.text({ text: 'Row 1 Item 1' });
topRow.add.text({ text: 'Row 1 Item 2' });
const bottomRow = mainContainer.add.flexContainer({
layout: {
flexDirection: 'row',
gap: 10,
alignItems: 'center',
width: '100%',
height: 'auto',
},
});
bottomRow.add.text({ text: 'Row 2 Item 1' });
bottomRow.add.text({ text: 'Row 2 Item 2' });

FlexContainer emits layout events you can listen to:

const container = this.add.flexContainer({
layout: { flexDirection: 'row', gap: 10 },
});
container.onLayoutComplete.connect(() => {
console.log('Layout calculation complete');
});

The UICanvas provides edge-based alignment for UI elements using a sophisticated 9-grid system, making it perfect for HUD elements, menus, and other screen-anchored UI components. It’s built on top of FlexContainer internally.

// Create a UI canvas
const ui = this.add.uiCanvas({
useAppSize: true, // Bind to application size
debug: true, // Show alignment guides
autoLayoutChildren: true, // Auto-configure child layouts
});
// Add elements with alignment
ui.addElement(this.make.text({ text: 'Top Left' }), { align: 'top left' });
ui.addElement(this.make.text({ text: 'Bottom Right' }), { align: 'bottom right' });
const ui = this.add.uiCanvas({
// Visual debugging
debug: false,
// Canvas padding
padding: { top: 20, left: 20, bottom: 20, right: 20 },
// Manual size (ignored if useAppSize is true)
size: { width: 800, height: 600 },
// Bind to application size
useAppSize: true,
// Layout configuration for the main container
layout: {
flexDirection: 'column',
justifyContent: 'space-between',
},
// Auto-configure child layouts
autoLayoutChildren: true,
});

UICanvas creates a sophisticated 9-grid layout system using FlexContainers:

// UICanvas internally creates:
// - topRow: FlexContainer (top alignment)
// - middleRow: FlexContainer (middle alignment)
// - bottomRow: FlexContainer (bottom alignment)
// Each row contains position containers for left/center/right alignment
// This creates 9 total positioning zones

UICanvas supports various edge alignments mapped to the 9-grid system:

  • Corners: ‘top left’, ‘top right’, ‘bottom left’, ‘bottom right’
  • Edges: ‘top’, ‘bottom’, ‘left’, ‘right’
  • Centers: ‘top center’, ‘bottom center’, ‘left center’, ‘right center’
  • Absolute center: ‘center’
// All these alignments work:
ui.addElement(element1, { align: 'top left' });
ui.addElement(element2, { align: 'left top' }); // Same as above
ui.addElement(element3, { align: 'center' });
ui.addElement(element4, { align: 'bottom right' });

Elements can be positioned with padding from their aligned edges:

ui.addElement(this.make.text({ text: 'Padded Corner' }), {
align: 'top right',
padding: { top: 20, right: 20 },
});
// Padding can also be specified as a point-like object
ui.addElement(someElement, {
align: 'bottom left',
padding: { x: 10, y: 10 }, // Applies to left/right and top/bottom
});

UICanvas and FlexContainer work seamlessly together:

const ui = this.add.uiCanvas({ useAppSize: true });
// Create a flex container for a bottom toolbar
const toolbar = this.make.flexContainer({
layout: {
gap: 20,
alignItems: 'center',
height: 48,
width: 'auto',
flexDirection: 'row',
},
});
toolbar.add.text({ text: 'Button 1' });
toolbar.add.text({ text: 'Button 2' });
toolbar.add.text({ text: 'Button 3' });
// Add the flex container to the UI canvas
ui.addElement(toolbar, { align: 'bottom center' });

UICanvas automatically handles layout updates through the @pixi/layout system:

const ui = this.add.uiCanvas({ useAppSize: true });
// Layout updates happen automatically when:
// - Elements are added or removed
// - App is resized (if useAppSize is true)
// - Child elements change their layout properties
// - Manual updateLayout() is called
ui.updateLayout(); // Force layout update if needed

Both FlexContainer and UICanvas support automatic layout configuration for children:

const container = this.add.flexContainer({
autoLayoutChildren: true, // default
layout: { flexDirection: 'row' },
});
// When autoLayoutChildren is true:
// - Children automatically get layout properties
// - Width and height are set to 'auto' if not specified
// - Children are marked as layout leaves for optimization

The layout system supports various value types for dimensions:

const container = this.add.flexContainer({
layout: {
width: 400, // Fixed pixel width
height: 'auto', // Auto-size to content
maxWidth: '80%', // Percentage of parent
minHeight: 100, // Minimum size constraint
flexGrow: 1, // Flex grow factor
flexShrink: 0, // Flex shrink factor
},
});

The @pixi/layout integration provides several performance benefits:

  1. Efficient Updates: Only recalculates when properties change
  2. Batched Calculations: Layout updates are batched for performance
  3. Optimized Rendering: Reduces unnecessary draw calls
  4. Memory Efficient: Reuses layout calculations where possible
// Layout updates are automatically optimized
const container = this.add.flexContainer({
layout: { flexDirection: 'row', gap: 10 },
});
// Multiple property changes are batched
container.gap = 20;
container.flexDirection = 'column';
// Layout recalculation happens once, not twice
  1. Choose the Right Tool:

    • Use FlexContainer for dynamic content that needs to flow and adapt
    • Use UICanvas for screen-anchored UI elements like HUDs and menus
  2. Performance:

    • Keep autoLayoutChildren: true unless you need manual control
    • Use appropriate size constraints (width: 'auto' vs fixed values)
    • Minimize deep nesting of layout containers
  3. Responsive Design:

    • Use useAppSize: true with UICanvas for responsive layouts
    • Leverage percentage values and ‘auto’ sizing for adaptive layouts
    • Use bindToAppSize or bindTo for reactive container sizing
  4. Layout Properties:

    • Prefer the layout object over individual property setters for multiple changes
    • Use percentage strings (‘50%’) for responsive sizing
    • Leverage ‘auto’ and ‘intrinsic’ sizing for content-driven layouts
  5. Debugging:

    • Use debug: true to visualize layout boundaries on UI Canvas
    • Monitor layout events for performance debugging
    • Use the browser’s performance tools to profile layout calculations