# Causes of Validation Failures for Missing React Props Interface in Stitch Skills

> Discover why React components in stitch-skills fail validation due to missing TypeScript Props interfaces. Learn how the validator identifies these errors.

- Repository: [Google Labs Code/stitch-skills](https://github.com/google-labs-code/stitch-skills)
- Tags: how-to-guide
- Published: 2026-07-16

---

**Validation fails when a React component in the stitch-skills framework lacks a TypeScript interface ending with `Props`, which the validator searches for using the TypeScript compiler API.**

The React Component Skill in the `google-labs-code/stitch-skills` repository enforces strict TypeScript validation to ensure JSON props match component definitions. When validation fails with a "Missing Props interface" error, it typically stems from specific naming convention violations or missing interface declarations in the component file. Understanding how the `validateComponent` function parses TypeScript source files reveals exactly why these validation failures occur.

## How the Validator Detects Missing Props Interfaces

The validation logic resides in [`plugins/stitch-build/skills/react-components/scripts/validate.js`](https://github.com/google-labs-code/stitch-skills/blob/main/plugins/stitch-build/skills/react-components/scripts/validate.js). When the skill receives a request to render a component, it invokes `validateComponent(componentDir, componentName, props)` to ensure the component adheres to the required contract.

### The TypeScript Compiler API Walk

The validator uses the TypeScript compiler API to parse the component file into an AST. In the `getPropsInterface` function (lines 6-26), it reads the source file and creates a TypeScript source file object:

```javascript
const source = fs.readFileSync(componentPath, 'utf8');
const sourceFile = ts.createSourceFile(
  componentPath,
  source,
  ts.ScriptTarget.Latest,
  true
);

```

It then traverses the AST using `ts.forEachChild`, looking for `InterfaceDeclaration` nodes:

```javascript
ts.forEachChild(sourceFile, (node) => {
  if (ts.isInterfaceDeclaration(node)) {
    if (node.name.text.endsWith('Props')) {
      interfaceName = node.name.text;
    }
  }
});

```

### The `Props` Suffix Requirement

The validation logic specifically checks for interface names ending with the string `Props` (line 19). If no interface matching this pattern is found, `getPropsInterface` returns `null`, triggering the error in `validateComponent` (lines 35-38):

```javascript
const propsInterface = getPropsInterface(componentPath);
if (!propsInterface) {
  throw new Error(`Missing Props interface for component ${componentName}`);
}

```

## Common Causes of Validation Failures

### Undeclared Props Interfaces

The most straightforward cause occurs when a component file contains no TypeScript interface at all. The validator expects every component to explicitly define its contract through an interface that describes the expected JSON props structure.

**Failing example** ([`src/components/Card.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/src/components/Card.tsx)):

```tsx
import React from "react";

const Card: React.FC<any> = (props) => (
  <div className="card">
    <h2>{props.title}</h2>
    <p>{props.content}</p>
  </div>
);

export default Card;

```

This component lacks any interface declaration, causing `getPropsInterface` to return `null` and the validator to throw: `"Missing Props interface for component Card"`.

### Non-Standard Interface Naming

The validator requires the interface name to end with `Props`. Using alternative naming conventions prevents the AST walker from recognizing the interface.

**Failing example**:

```tsx
export interface CardProperties {
  title: string;
  content: string;
}

```

Because `CardProperties` does not end with `Props`, the validation fails even though the interface is correctly exported.

### Component File Resolution Errors

Before checking for the Props interface, the validator verifies the component file exists (lines 31-33):

```javascript
const componentPath = path.join(componentDir, `${componentName}.tsx`);
if (!fs.existsSync(componentPath)) {
  throw new Error(`Component file not found: ${componentPath}`);
}

```

If the file is missing or misnamed (for example, [`card.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/card.tsx) instead of [`Card.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/Card.tsx)), validation fails with a file-not-found error before the Props interface check occurs.

## Example Scenarios and Solutions

**Correct implementation** (following the template in [`component-template.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/component-template.tsx)):

```tsx
import React from "react";

export interface CardProps {
  title: string;
  content: string;
}

const Card: React.FC<CardProps> = ({ title, content }) => (
  <div className="card">
    <h2>{title}</h2>
    <p>{content}</p>
  </div>
);

export default Card;

```

This satisfies the validation rules defined in [`SKILL.md`](https://github.com/google-labs-code/stitch-skills/blob/main/SKILL.md) (lines 33-35) and allows the skill to proceed with type checking. The interface is exported, follows the `{ComponentName}Props` naming convention, and resides in the expected file location.

## Summary

- **The React Component Skill** requires every component to export a TypeScript interface ending with `Props` to validate incoming JSON data.
- **The validator** uses the TypeScript compiler API to parse `src/components/{ComponentName}.tsx` and searches for interface declarations matching the `Props` suffix pattern.
- **Validation fails** when the interface is missing, uses an incorrect name (like `Properties` instead of `Props`), or when the component file cannot be found at the expected path.
- **The error message** `"Missing Props interface for component {Name}"` originates from [`validate.js`](https://github.com/google-labs-code/stitch-skills/blob/main/validate.js) lines 36-38 when the AST walker finds no matching interface.

## Frequently Asked Questions

### Why must the interface name end with "Props"?

The `getPropsInterface` function in [`validate.js`](https://github.com/google-labs-code/stitch-skills/blob/main/validate.js) explicitly checks for interface names ending with `Props` using `node.name.text.endsWith('Props')`. This convention ensures the validator can reliably distinguish between component props and other interfaces (like state interfaces or utility types) that might exist in the file.

### Can I use a type alias instead of an interface?

According to the source code in [`validate.js`](https://github.com/google-labs-code/stitch-skills/blob/main/validate.js), the validator only searches for `InterfaceDeclaration` nodes using `ts.isInterfaceDeclaration(node)`. Type aliases declared with `type` are not checked, so using `type CardProps = { ... }` will cause validation to fail with a missing interface error.

### What file path should my component use?

The skill expects components at `src/components/{ComponentName}.tsx` with exact case matching, as implemented in `validateComponent` (line 30). For a component named `Card`, the file must be [`src/components/Card.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/src/components/Card.tsx), not [`card.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/card.tsx) or [`Card/index.tsx`](https://github.com/google-labs-code/stitch-skills/blob/main/Card/index.tsx).

### How do I debug validation failures locally?

Run the validation script directly using Node.js before deployment. The [`validate.js`](https://github.com/google-labs-code/stitch-skills/blob/main/validate.js) file exports `validateComponent`, which you can call with your local components directory path to verify your Props interface is detected correctly before the skill processes the request.