Feature Guide

Matrix Questions for Surveys

January 2026 · 9 min read

Rate these five aspects on a scale of 1-5. You've seen this in every survey. It's a matrix question - rows of items, columns of ratings, and a grid that lets respondents answer multiple related questions efficiently.

Matrix questions are essential for employee surveys, product feedback, customer satisfaction research, and any situation where you need to measure the same scale across multiple items. This guide shows how to build them in FormTs.

Basic Matrix Question

A matrix question has rows (the items being rated) and columns (the rating options). Here's a simple customer satisfaction example:

form.addRow(row => {
  row.addMatrixQuestion('satisfaction', {
    label: 'Rate your satisfaction with each aspect:',
    rows: [
      { id: 'quality', label: 'Product Quality' },
      { id: 'service', label: 'Customer Service' },
      { id: 'value', label: 'Value for Money' },
      { id: 'delivery', label: 'Delivery Speed' }
    ],
    columns: [
      { id: '1', label: 'Very Dissatisfied' },
      { id: '2', label: 'Dissatisfied' },
      { id: '3', label: 'Neutral' },
      { id: '4', label: 'Satisfied' },
      { id: '5', label: 'Very Satisfied' }
    ]
  });
});

Each row is an aspect to rate. Each column is a rating option. Respondents click once per row to select their rating. The result is structured data you can analyze.

Likert Scales

The most common matrix format is a Likert scale - agreement levels from "Strongly Disagree" to "Strongly Agree". Perfect for measuring attitudes and opinions.

form.addRow(row => {
  row.addMatrixQuestion('agreement', {
    label: 'Please indicate your level of agreement:',
    rows: [
      { id: 'recommend', label: 'I would recommend this product to others' },
      { id: 'repurchase', label: 'I would purchase this product again' },
      { id: 'expectations', label: 'The product met my expectations' },
      { id: 'value', label: 'The product is good value for the price' }
    ],
    columns: [
      { id: 'sd', label: 'Strongly Disagree' },
      { id: 'd', label: 'Disagree' },
      { id: 'n', label: 'Neutral' },
      { id: 'a', label: 'Agree' },
      { id: 'sa', label: 'Strongly Agree' }
    ],
    striped: true
  });
});

The striped: true option adds alternating row colors for better readability. Essential when you have many rows.

Pro tip

Keep Likert scales consistent across your survey. If one question uses 5 points, use 5 points everywhere. Mixing 5-point and 7-point scales confuses respondents and makes data analysis harder.

Required Rows

Not all rows need to be required. Maybe some items are "nice to know" while others are essential for your analysis.

form.addRow(row => {
  row.addMatrixQuestion('priorities', {
    label: 'Rate the importance of each factor:',
    rows: [
      { id: 'price', label: 'Price', isRequired: true },
      { id: 'quality', label: 'Quality', isRequired: true },
      { id: 'brand', label: 'Brand Reputation' },
      { id: 'warranty', label: 'Warranty Coverage' }
    ],
    columns: [
      { id: 'low', label: 'Low' },
      { id: 'medium', label: 'Medium' },
      { id: 'high', label: 'High' },
      { id: 'critical', label: 'Critical' }
    ]
  });
});

Rows with isRequired: true must be answered before the form can be submitted. Rows without it are optional - respondents can skip them.

Multiple Selection Mode

By default, each row allows one selection (single choice). But sometimes you need multiple selections - like mapping skills to team members.

form.addRow(row => {
  row.addMatrixQuestion('skills', {
    label: 'Which skills apply to each team member?',
    selectionMode: 'multiple',
    rows: [
      { id: 'alice', label: 'Alice' },
      { id: 'bob', label: 'Bob' },
      { id: 'charlie', label: 'Charlie' }
    ],
    columns: [
      { id: 'frontend', label: 'Frontend' },
      { id: 'backend', label: 'Backend' },
      { id: 'design', label: 'Design' },
      { id: 'devops', label: 'DevOps' }
    ]
  });
});

With selectionMode: 'multiple', respondents can check multiple columns per row. The value for each row becomes an array of selected column IDs.

Row Descriptions

Sometimes row labels need clarification. Add descriptions that appear below the main label.

form.addRow(row => {
  row.addMatrixQuestion('features', {
    label: 'How important are these features?',
    rows: [
      {
        id: 'sync',
        label: 'Real-time Sync',
        description: 'Changes appear instantly across all devices'
      },
      {
        id: 'offline',
        label: 'Offline Mode',
        description: 'Work without internet connection'
      },
      {
        id: 'collab',
        label: 'Collaboration',
        description: 'Multiple users editing simultaneously'
      }
    ],
    columns: [
      { id: 'not', label: 'Not Important' },
      { id: 'nice', label: 'Nice to Have' },
      { id: 'important', label: 'Important' },
      { id: 'essential', label: 'Essential' }
    ]
  });
});

Descriptions help respondents understand exactly what they're rating, reducing confusion and improving data quality.

See matrix questions in our feedback form templates.

Accessing Values

Matrix questions return structured data. Here's how to work with it:

// Get value for specific row
const qualityRating = form.matrixQuestion('satisfaction')?.getRowValue('quality');
// Returns: 'satisfied' (column id) or null

// Check if all required rows are answered
const isComplete = form.matrixQuestion('satisfaction')?.areAllRequiredRowsAnswered();
// Returns: true/false

// Check specific row
const isAnswered = form.matrixQuestion('satisfaction')?.isRowAnswered('quality');
// Returns: true/false

// Get all values
const allValues = form.matrixQuestion('satisfaction')?.value();
// Returns: { quality: '4', service: '5', value: '3', delivery: '4' }

The getRowValue() method is useful for conditional logic. The areAllRequiredRowsAnswered() method helps with validation.

Conditional Follow-Up

Low ratings deserve follow-up. Show a text field when someone gives a poor rating on a specific row.

form.addRow(row => {
  row.addMatrixQuestion('satisfaction', {
    label: 'Rate each aspect:',
    rows: [
      { id: 'product', label: 'Product' },
      { id: 'support', label: 'Support' },
      { id: 'pricing', label: 'Pricing' }
    ],
    columns: [
      { id: '1', label: '1' },
      { id: '2', label: '2' },
      { id: '3', label: '3' },
      { id: '4', label: '4' },
      { id: '5', label: '5' }
    ]
  });
});

// Follow-up for low ratings
form.addRow(row => {
  row.addTextarea('supportFeedback', {
    label: 'What could we improve about our support?',
    isVisible: () => {
      const rating = form.matrixQuestion('satisfaction')?.getRowValue('support');
      return rating === '1' || rating === '2';
    },
    rows: 3
  });
});

This pattern is powerful for feedback surveys. You get structured ratings for analysis plus detailed explanations for the problem areas.

Employee Survey Example

Matrix questions shine in employee surveys. Here's a workplace satisfaction measurement:

form.setTitle(() => 'Employee Satisfaction Survey');

form.addRow(row => {
  row.addMatrixQuestion('workplace', {
    label: 'Rate your agreement with each statement:',
    rows: [
      { id: 'valued', label: 'I feel valued at work', isRequired: true },
      { id: 'growth', label: 'I have opportunities for growth', isRequired: true },
      { id: 'balance', label: 'I have good work-life balance', isRequired: true },
      { id: 'tools', label: 'I have the tools I need to do my job' },
      { id: 'communication', label: 'Communication is clear and open' }
    ],
    columns: [
      { id: 'sd', label: 'Strongly Disagree' },
      { id: 'd', label: 'Disagree' },
      { id: 'n', label: 'Neutral' },
      { id: 'a', label: 'Agree' },
      { id: 'sa', label: 'Strongly Agree' }
    ],
    striped: true
  });
});

The five-point Likert scale is standard for employee surveys. Required rows ensure you get data on the most important factors.

Complete Example

Here's a product feedback survey with a matrix question and conditional follow-up for low ratings:

export function productFeedback(form: FormTs) {
  form.setTitle(() => 'Product Feedback Survey');

  form.addRow(row => {
    row.addMatrixQuestion('experience', {
      label: 'Rate your experience with our product:',
      rows: [
        { id: 'ease', label: 'Ease of Use', isRequired: true },
        { id: 'features', label: 'Features', isRequired: true },
        { id: 'performance', label: 'Performance', isRequired: true },
        { id: 'design', label: 'Visual Design' },
        { id: 'docs', label: 'Documentation' }
      ],
      columns: [
        { id: '1', label: 'Poor' },
        { id: '2', label: 'Fair' },
        { id: '3', label: 'Good' },
        { id: '4', label: 'Very Good' },
        { id: '5', label: 'Excellent' }
      ],
      striped: true
    });
  });

  // Follow-up for any low ratings
  form.addRow(row => {
    row.addTextarea('improvements', {
      label: 'What could we improve?',
      placeholder: 'Tell us about any issues you experienced...',
      isVisible: () => {
        const matrix = form.matrixQuestion('experience');
        if (!matrix) return false;

        const values = matrix.value() || {};
        return Object.values(values).some(v => v === '1' || v === '2');
      },
      rows: 3
    });
  });

  form.configureSubmitButton({ label: 'Submit Feedback' });
}

Best Practices

Keep It Focused

Don't put 20 items in one matrix. It's overwhelming and leads to "straight-lining" (selecting the same answer for everything). Group related items into separate matrices of 5-7 items each.

Consistent Scales

Use the same number of points across all matrices in a survey. If you need different scales (agreement vs. satisfaction vs. importance), make sure the number of options matches.

Clear Labels

Column labels should be unambiguous. "Good" vs "Very Good" vs "Excellent" is subjective. Numbers (1-5) with endpoint labels ("Poor" to "Excellent") are clearer.

Mobile Considerations

Matrix questions can be tricky on mobile. Wide grids require horizontal scrolling. Keep column count reasonable (5-7 max) and test on phones before deploying.

Common Questions

How many columns should a matrix have?

5-7 columns is typical. Fewer than 5 doesn't capture enough nuance. More than 7 is hard to distinguish meaningfully and causes mobile display issues. For Likert scales, 5 points is standard; 7 points gives more granularity if needed.

Should I include a neutral/middle option?

Usually yes. A 5-point scale with a neutral middle (Strongly Disagree, Disagree, Neutral, Agree, Strongly Agree) is standard. Forcing people to pick a side (4 or 6 points) can work but may frustrate respondents who genuinely have no opinion.

How do I prevent straight-lining?

Straight-lining happens when respondents click the same column for every row. Reduce it by: keeping matrices short (5-7 rows), mixing positive and negative statements, using clear descriptions, and not requiring all rows. Some straight-lining is unavoidable in any survey.

Can I randomize row order?

Row order in the config determines display order. If you need randomization, shuffle the rows array before passing it to the config. This can reduce order bias in research surveys.

What's the difference between matrix and rating scale?

A rating scale is a single item with a numeric scale (like NPS 0-10). A matrix is multiple items sharing the same scale in a grid. Use rating scale for standalone questions, matrix when you have several related items to rate on the same dimensions.

Ready to Build Survey Matrices?

Create professional surveys with Likert scales and grid questions.