Skip to main content
August 16, 2024 | Derek Watson | Engineering | How To | Web In Anvil2, we have introduced new components and features to make creating dynamic, responsive page layouts a breeze. Read on to learn more! Fork this codesandbox to play around with some of the new features! This was used during a tech talk on July 2nd, 2024.

Layout changes from Anvil → Anvil2

Summary

  • Enable easy use of Flexbox and CSS Grid
    • Two new component – Flex and Grid
    • All Anvil2 components have some control over layout using layout utils props
  • Simplified APIs
  • Flexibility and responsiveness baked-in

Page component updates

See Anvil2 Page Implementation Docs
  • Simplified API
  • Headers, footers, and action toolbars should now be created independently as part of the layout
  • Sidebar and Panel are now sub-components
  • Background color can be controlled using CSS (style or className)
  • Better responsive behavior coming soon!
<Page>
  <Page.Sidebar>
    <Page.SidebarHeader>
      <Text variant="headline" el="h2">
        Sidebar Header
      </Text>
    </Page.SidebarHeader>
    <SideNav>
      <SideNav.Link id="01" href="#">
        Nav Link
      </SideNav.Link>
    </SideNav>
  </Page.Sidebar>
  <Page.Content>
    {/* page content (including the header, action toolbar, footer, etc.) */}
  </Page.Content>
</Page>

Layout component updates

See Anvil2 Layout Implementation Docs
  • Simplified API with Layout and Layout.Item used to create columns
  • Responsiveness built-in, and customizable
  • Three density variants available using variant prop: "default", "narrow", and "wide"
<Layout variant="narrow">
  <Layout.Item span={12}>{/\* full-width content... \*/}</Layout.Item>
  <Layout.Item span={3}>{/\* one-quarter-width content... \*/}</Layout.Item>
  <Layout.Item span={9}>{/\* three-quarter-width content... \*/}</Layout.Item>
</Layout>

Column structure and responsive behavior​

Layouts use a 12-column structure that reduces columns based on the size of the layout. Note that the values below are based on the width of the Layout, not the entire screen (using container queries). This is important if the page includes a sidebar or panel.
  • On sm screens (and below), there are four columns
  • On md screens and up, there are twelve columns
Responsive props can be used to override the span value at certain breakpoints (and higher).
<Layout.Item
  // <640px = full-width
  span={4}
  // 640px -> 767px = 3/4 width
  sm={3}
  // 768px -> 1023px = 1/3 width
  md={4}
  // 1024px -> 1279px = 1/2 width
  lg={6}
  // 1280px -> 1535px = 2/3 width
  xl={8}
  // >=1536px = 3/4 width
  xxl={9}
>
  {/*...content */}
</Layout.Item>

New layout features in Anvil2

Flex and Grid components

See the Flex Implementation Docs and Grid Implementation Docs. Flex is similar to Stack, with some minor differences. A Flex can be used both as a flex container and item, so instead of using Stack.Item, use another Flex or another Anvil2 component. This greatly reduces the amount of code required to build flex layouts!

Anvil (legacy) example

<Stack direction="column" alignItems="center">
  <Stack.Item>
    <Headline />
  </Stack.Item>
  <Stack.Item>
    <Stack spacing="1">
      <Stack.Item fill>
        <Text />
      </Stack.Item>
      <Stack.Item alignSelf="flex-start">
        <Button />
      </Stack.Item>
    </Stack>
  </Stack.Item>
</Stack>

Anvil2 example

<Flex direction="column" alignItems="center">
  <Text variant="headline" />
  <Flex gap="2">
    <Text flexGrow="1" />
    <Button alignSelf="flex-start" />
  </Flex>
</Flex>
The new Grid component can be used to create CSS Grid layouts (not to be confused with the Anvil Grid). Grid components can also be used as grid containers or items, and any Anvil2 component can use special grid props to control its columns and rows.
<Grid
  templateColumns="repeat(5, 1fr)"
  autoRows="minmax(3rem, auto)"
  columnGap="4"
  rowGap="2"
>
  <Card gridColumn="1 / 6" />
  <Card gridRow="2 / 5" />
  <Card gridArea="2 / 2 / 5 / 6" />
</Grid>

Responsive props of Flex and Grid​

Similar to the Layout.Item, responsive props are also available to override any of the Flex and Grid props at different breakpoints.
<Grid
  md={{ templateColumns: "repeat(5, 1fr)", columnGap: "4" }}
  autoRows="minmax(3rem, auto)"
  rowGap="2"
>
  <Card sm={{ gridColumn: "1 / 6" }} />
  <Card sm={{ gridRow: "2 / 5" }} />
  <Flex
    direction="column"
    alignItems="center"
    md={{ gridArea: "2 / 2 / 5 / 6" }}
  >
    <Text variant="headline" />
    <Flex gap="2">
      <Text flexGrow="1" />
      <Button alignSelf="flex-start" />
    </Flex>
  </Flex>
</Grid>

Layout Props

See Layout Props Utilities documentation. All Anvil2 components other than Page and Layout extend Layout type, which enable them to control various flex and grid properties. props at different breakpoints.
type Layout = {
    // flex only
    flex?: CSSProperties["flex"];
    flexDirection?: CSSProperties["flexDirection"];
    flexGrow?: CSSProperties["flexGrow"];
    flexShrink?: CSSProperties["flexShrink"];
    flexBasis?: CSSProperties["flexBasis"];

    // grid only
    gridArea?: CSSProperties["gridArea"];
    gridColumn?: CSSProperties["gridColumn"];
    gridRow?: CSSProperties["gridRow"];
    gridColumnStart?: CSSProperties["gridColumnStart"];
    gridColumnEnd?: CSSProperties["gridColumnEnd"];
    gridRowStart?: CSSProperties["gridRowStart"];
    gridRowEnd?: CSSProperties["gridRowEnd"];

    // both
    alignContent?: CSSProperties["alignContent"];
    alignItems?: CSSProperties["alignItems"];
    alignSelf?: CSSProperties["alignSelf"];
    columnGap?:0” -> “14|half”;
    gap?:0” -> “14|half”;
    justifyContent?: CSSProperties["justifyContent"];
    justifyItems?: CSSProperties["justifyItems"];
    justifySelf?: CSSProperties["justifySelf"];
    order?: CSSProperties["order"];
    placeContent?: CSSProperties["placeContent"];
    placeItems?: CSSProperties["placeItems"];
    placeSelf?: CSSProperties["placeSelf"];
    rowGap?:0” -> “14|half”;

    // responsive overrides to all of the props above
    sm: Omit<Layout, "sm" | "md" | "lg" | "xl" | "xxl">
    md: Omit<Layout, "sm" | "md" | "lg" | "xl" | "xxl">
    lg: Omit<Layout, "sm" | "md" | "lg" | "xl" | "xxl">
    xl: Omit<Layout, "sm" | "md" | "lg" | "xl" | "xxl">
    xxl: Omit<Layout, "sm" | "md" | "lg" | "xl" | "xxl">
}
Several of the props in the Layout type only apply to flex or grid containers. The Card component is a flex container by default. Other components would need display: flex or display: grid added to use the following props:
type LayoutPropsForContainersOnly = {
    flexDirection?: CSSProperties["flexDirection"];
    alignContent?: CSSProperties["alignContent"];
    alignItems?: CSSProperties["alignItems"];
    columnGap?:0” -> “14|half”;
    gap?:0” -> “14|half”;
    justifyContent?: CSSProperties["justifyContent"];
    justifyItems?: CSSProperties["justifyItems"];
    placeContent?: CSSProperties["placeContent"];
    placeItems?: CSSProperties["placeItems"];
    rowGap?:0” -> “14|half”;
}

Putting it all together

These components and props work together to make creating flexible, responsive layouts a breeze in Anvil2!
const ExamplePage = () => {
  const [panelIsOpen, setPanelIsOpen] = useState(false);
  const gridData = [
    /*... some objects ...*/
  ];
  return (
    <Page>
      <Page.Sidebar>
        <Page.SidebarHeader>
          <Text variant="headline" el="h2">
            Sidebar Header
          </Text>
        </Page.SidebarHeader>
        <SideNav>
          <SideNav.Item id="1" active>
            First Page
          </SideNav.Item>
        </SideNav>
      </Page.Sidebar>
      <Page.Panel open={panelIsOpen}>{/* panel content */}</Page.Panel>
      <Page.Content>
        <Layout fluid>
          <Layout.Item span={12}>
            <Flex direction="column" gap="2" md={{ direction: "row" }}>
              <Text variant="headline" el="h1" md={{ flexGrow: "1" }}>
                Page Title
              </Text>
              <Card direction="column" md={{ alignSelf: "flex-start" }}>
                <Text variant="eyebrow">Card header</Text>
                <Text>Card content</Text>
              </Card>
            </Flex>
          </Layout.Item>
          <Layout.Item span={4} md={5} lg={4}>
            {/*  small column content      */}
            {/*  xs/sm     =   full-width  */}
            {/*  md        =   5/12 width   */}
            {/*  lg/xl/xxl =   1/3 width   */}
          </Layout.Item>
          <Layout.Item span={4} md={7} lg={8}>
            {/*  large column content      */}
            {/*  xs/sm     =   full-width  */}
            {/*  md        =   7/12 width   */}
            {/*  lg/xl/xxl =   2/3 width   */}
            <Grid
              gap="2"
              sm={{ templateColumns: "repeat(2, 1fr)" }}
              md={{ templateColumns: "repeat(3, 1fr)" }}
              xl={{ templateColumns: "repeat(4, 1fr)" }}
            >
              {gridData.map((item) => (
                <Card>{/* render gridData item */}</Card>
              ))}
            </Grid>
          </Layout.Item>
        </Layout>
      </Page.Content>
    </Page>
  );
};
Last modified on January 23, 2026