Data Analysis

Google Sheets Array Literal Header Row Formula Guide

Marc SeanJune 9, 20266 min read

Google's official documentation covers array literals at a surface level but never explicitly documents this pattern. What the Sheets help docs state, as of June 2026, is the general rule: in US-locale spreadsheets, semicolons separate rows in an array literal and commas separate columns. The header trick is a direct consequence of that - the string before the semicolon becomes row 1, and the ARRAYFORMULA output stacks below it.

How the Array Literal Syntax Works

In Google Sheets, {A; B} concatenates A and B vertically into a single column. {A, B} stacks them side by side into a row. The header pattern exploits the vertical case:

={"Revenue"; ARRAYFORMULA('P&L'!C2:C)}

Row 1 gets the string "Revenue". Rows 2 through N come from column C of the P&L tab. The ARRAYFORMULA wrapper is what allows the open-ended range (C2:C) to expand dynamically - without it, an open range inside an array literal throws #VALUE!.

You can technically skip ARRAYFORMULA with fixed ranges:

-- Works for a known range, but breaks if rows are added
={"Revenue"; 'P&L'!C2:C24}

In practice, finance models grow. Use ARRAYFORMULA and wrap the inner expression with an IF to terminate cleanly:

={"Revenue"; ARRAYFORMULA(IF('P&L'!A2:A="","", 'P&L'!C2:C))}

Without the IF, the formula extends to approximately row 1,000,000 (Google Sheets supports up to 10 million cells per spreadsheet), filling every blank row with empty strings. That's what breaks your SUMIFS, blows up chart ranges, and causes pivot tables to show phantom rows at the bottom of quarterly board packs.

Multi-Column Header Syntax

For multiple columns, the header row is comma-separated strings before the semicolon, and the data side is a matching array:

={"SKU","Revenue ($M)","COGS ($M)","Gross Profit","Margin %";
  ARRAYFORMULA(IF('P&L'!A2:A="","",{
    'P&L'!A2:A,
    'P&L'!C2:C,
    'P&L'!D2:D,
    'P&L'!C2:C - 'P&L'!D2:D,
    TEXT(('P&L'!C2:C - 'P&L'!D2:D) / 'P&L'!C2:C, "0.0%")
  }))}

This is a contribution margin table by SKU - 5 columns, inline calculation, terminates when the SKU column runs out. The header count must match the column count in the data array exactly, or Sheets returns #VALUE!. For a quarterly board pack where you're building a horizontal summary across 5 forecast periods:

={"Period","Revenue","EBITDA","EBITDA Margin";
  ARRAYFORMULA(IF('Assumptions'!B3:F3="","",{
    'Assumptions'!B3:F3,
    'P&L'!B5:F5,
    'P&L'!B15:F15,
    TEXT('P&L'!B15:F15 / 'P&L'!B5:F5, "0.0%")
  }))}

A model with $18.4M projected revenue at a 38.5% EBITDA margin in Year 1 would render "38.5%" automatically as that margin compresses or expands in the assumptions.

The Locale Problem Nobody Warns You About

This is the silent model-breaker. In ~40 countries that use a comma as the decimal separator (Germany, France, the Netherlands, Brazil when working in PT-BR Sheets), Google Sheets flips the array literal separators:

  • US locale: semicolon = new row, comma = new column
  • European locale: semicolon = new column, backslash \ = new row

So {"Header"; ARRAYFORMULA(...)} in a European-locale file puts "Header" and the ARRAYFORMULA output side by side on the same row, not stacked. Your 5-column contribution margin table becomes a 10-column mess.

The locale is set at the file level under File > Settings > Locale, not the user's system locale. When you share a model with a bank syndicate or PE sponsor in Frankfurt and they open it in their Sheets environment, the locale may differ from yours. According to Google's Sheets documentation, the separator behavior is tied to the spreadsheet's locale setting, not the viewer's regional settings - but this gets complicated when files are duplicated or imported.

Practical fix: add a cell note documenting the expected locale, or test with a throwaway file before distributing a model with array literal headers.

VSTACK: The 2022 Alternative

Google released VSTACK in late 2022. The functional equivalent of the header pattern is:

=VSTACK({"SKU","Revenue ($M)","Margin %"},
  ARRAYFORMULA(IF('P&L'!A2:A="","",{
    'P&L'!A2:A,
    'P&L'!C2:C,
    TEXT(('P&L'!C2:C-'P&L'!D2:D)/'P&L'!C2:C,"0.0%")
  }))
)

VSTACK is locale-safe and handles mixed-width arrays more gracefully in edge cases. The trade-off is portability: VSTACK requires a Sheets version that supports it (unavailable before 2022), and while Excel 365 has its own VSTACK, the behavior differs enough that a model designed around it won't translate cleanly if you're maintaining parallel Excel versions for bank presentations.

{"header"; ARRAYFORMULA}VSTACK
Locale-safeNoYes
Sheets version requiredAny2022+
Blank row suppressionRequiredOften still needed
Readable syntaxModeratelyCleaner
Excel compatibilityNoDifferent behavior

For models that will never leave Google Sheets and your Sheets file is in US locale, either works. For anything shared internationally or maintained in both Sheets and Excel, VSTACK is lower-risk.

Where the ARRAYFORMULA Scope Actually Matters

A frequent mistake: wrapping the outer array literal in ARRAYFORMULA instead of the inner range expression.

-- Wrong: ARRAYFORMULA is on the outside, doing nothing useful
=ARRAYFORMULA({"Revenue"; 'P&L'!C2:C})

-- Correct: ARRAYFORMULA expands the range inside the array
={"Revenue"; ARRAYFORMULA('P&L'!C2:C)}

-- Correct: ARRAYFORMULA expands a calculation
={"Gross Profit ($M)"; ARRAYFORMULA('P&L'!C2:C - 'P&L'!D2:D)}

The ARRAYFORMULA needs to sit around the expression that requires expansion - the subtraction across two open ranges, the IF condition, the TEXT formatting. Moving it to the outside doesn't break the formula, but it also doesn't fix the expansion problem if your inner expression contains arithmetic on ranges.

When to Use This Pattern

Reach for it when you need a single-formula data block with a header - typically for:

  • Summary tabs pulling labeled columns from a P&L, balance sheet, or cash flow
  • QUERY source ranges where the first row must be a header string (not a cell reference)
  • Waterfall or bridge chart data tables where each row feeds a labeled chart series
  • Sensitivity output tables combining scenario names with NPV or IRR calculations from a bank syndicate DCF

Skip it when your header and data are intentionally separated across rows in a structured model layout. Most multi-tab financial models keep headers in their own row for clarity, and the array literal approach is more useful in summary/output contexts than in the core model mechanics.

If you're generating these output tables programmatically - say, pulling from a live data source into a labeled Sheets table - ModelMonkey handles the header-and-data construction for you, including the IF termination logic that most hand-built formulas miss.


Frequently Asked Questions