# xstyle vs className vs style Props in Astryx Components: The Complete Guide

> Master Astryx styling with xstyle className and style props. Understand compile-time StyleX objects arbitrary CSS class strings and inline React objects to optimize your component styling.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: deep-dive
- Published: 2026-07-02

---

**Astryx components accept three distinct styling mechanisms: `xstyle` for compile-time StyleX objects with pseudo-class support, `className` for arbitrary CSS class strings appended after internal styles, and `style` for inline React objects that override all other values.**

In the `facebook/astryx` repository, the component architecture exposes a three-layer styling system that balances design-system consistency with flexible overrides. Understanding how `xstyle`, `className`, and `style` interact is essential for optimizing bundle size via static analysis while maintaining compatibility with utility-first CSS workflows.

## Understanding the xstyle Prop

The `xstyle` prop is **not** a CSS class name string. Instead, it accepts typed `StyleX` objects created via `stylex.create()`, enabling compile-time optimizations that standard CSS cannot achieve.

### Compile-Time Processing and Static Analysis

When you pass an `xstyle` value, the component forwards it to `stylex.props()` for merging at **compile-time**. This processing enables dead-code elimination, automatic deduplication of styles, and deterministic class name generation. According to the styling documentation in `packages/cli/docs/styling.doc.mjs` (lines 38-44), the `xstyle` prop supports pseudo-classes like `:hover` and `:focus-visible` expressed directly inside the `stylex.create` object.

Only values produced by `stylex.create()` are accepted—attempting to pass a raw string or inline style object triggers a TypeScript error. This type safety ensures design-token consistency across the application.

### Implementation in Core Components

In [`packages/core/src/Table/TableHeader.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Table/TableHeader.tsx), the component forwards received `xstyle` values to the StyleX compiler:

```tsx
export function TableHeader({ref, children, xstyle}: TableHeaderProps) {
  return (
    <thead
      ref={ref}
      {...mergeProps(themeProps('table-header'), stylex.props(xstyle))}
    >
      {children}
    </thead>
  );
}

```

This pattern appears throughout the codebase, including in [`packages/core/src/ToggleButton/ToggleButtonGroup.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/ToggleButton/ToggleButtonGroup.tsx) where the `xstyle?: StyleXStyles` type is defined, and [`packages/lab/src/Stepper/Stepper.tsx`](https://github.com/facebook/astryx/blob/main/packages/lab/src/Stepper/Stepper.tsx) which demonstrates passing `xstyle` through nested component hierarchies.

## How className Works in Astryx

The `className` prop accepts arbitrary strings of CSS class names, including Tailwind utilities or custom framework classes. Unlike `xstyle`, these values are **not** processed by the StyleX compiler.

Classes passed via `className` are appended **after** the component’s internal class list, as documented in `packages/cli/docs/styling.doc.mjs` (lines 30-33). This ordering allows utility-first classes to layer on top of the component’s built-in token classes without interfering with the design system’s base styles.

## The style Prop and Specificity

The `style` prop accepts plain React style objects (e.g., `{margin: 8, backgroundColor: 'red'}`). These inline styles are merged **after** both `xstyle` and `className`, giving them the highest specificity in the cascade.

Because inline styles win on any overlapping property, use this prop for one-off pixel values or dynamic calculations where defining a CSS class would be excessive.

## Processing Order and Specificity Hierarchy

Astryx applies styles in the following sequence, with each layer potentially overriding the previous:

1. **`xstyle`** – StyleX rules compiled to deterministic class names
2. **`className`** – Arbitrary CSS classes appended to the class list
3. **`style`** – Inline styles applied last, winning specificity conflicts

This hierarchy means a `style={{backgroundColor: 'pink'}}` declaration will override a background color set via `xstyle`, regardless of the order props are declared in JSX.

## Practical Code Examples

### Component-Specific Overrides with xstyle

```tsx
import * as stylex from '@stylexjs/stylex';
import {Card, Button} from '@astryxdesign/core';

const overrides = stylex.create({
  card: {
    maxWidth: 400,
    marginBlock: 16,
    ':hover': { '@media (hover: hover)': { boxShadow: '0 4px 12px rgba(0,0,0,0.1)' } },
  },
  saveButton: {
    alignSelf: 'flex-end',
  },
});

export function Example() {
  return (
    <>
      <Card xstyle={overrides.card}>Content</Card>
      <Button label="Save" xstyle={overrides.saveButton} />
    </>
  );
}

```

*Source: `packages/cli/docs/styling.doc.mjs` (lines 48-57)*

### Utility-First Styling with className

```tsx
<div className="flex gap-3 p-4 bg-surface text-primary rounded-container">
  <Button label="Save" variant="primary" />
  <Button label="Cancel" variant="secondary" />
</div>

```

### Inline Tweaks with style

```tsx
<Card
  xstyle={overrides.card}
  style={{border: '2px solid var(--color-accent)'}}
>
  Content
</Card>

```

### Combining All Three Props

```tsx
<Button
  xstyle={overrides.saveButton}
  className="hover:scale-105 transition-transform"
  style={{backgroundColor: 'var(--color-success)'}}
/>

```

In this example, the button receives StyleX token-based styling (`xstyle`), Tailwind utility classes for hover effects (`className`), and an explicit inline background color (`style`). The final CSS cascade follows the order: StyleX → Tailwind → Inline.

## When to Use Each Prop

| Situation | Recommended Prop |
|-----------|------------------|
| Override design tokens (spacing, color, border radius) while preserving pseudo-class support | `xstyle` |
| Add layout utilities or integrate external CSS frameworks (Tailwind) | `className` |
| Apply single, one-off pixel values or CSS properties not covered by the design system | `style` |

## Summary

- **`xstyle`** accepts StyleX objects from `stylex.create()`, enabling compile-time deduplication, dead-code elimination, and pseudo-class support via `stylex.props()` processing.
- **`className`** appends arbitrary CSS class strings after internal component classes, supporting utility-first frameworks like Tailwind.
- **`style`** applies inline React style objects with highest specificity, overriding any conflicting properties from `xstyle` or `className`.
- The processing order is `xstyle` → `className` → `style`, with each subsequent layer winning specificity conflicts.

## Frequently Asked Questions

### Can I use Tailwind CSS with Astryx components?

Yes. Pass Tailwind utility classes to the `className` prop. These classes are appended after the component’s internal StyleX-generated classes, allowing utilities to override layout and spacing while preserving the component’s design-token base styles.

### Why does my inline style override my xstyle definition?

Because the `style` prop is merged **after** `xstyle` in the component’s internal logic. Inline styles have higher specificity in CSS than class-based styles, so a `style={{backgroundColor: 'red'}}` declaration will always win over a background color defined in your `xstyle` object, regardless of source order.

### Can I pass a CSS class string to the xstyle prop?

No. The `xstyle` prop is strictly typed to accept only `StyleX` objects created via `stylex.create()`. Passing a string like `xstyle="my-class"` results in a TypeScript compilation error. Use the `className` prop for arbitrary CSS class strings.

### Does xstyle support responsive breakpoints and media queries?

Yes. StyleX supports media queries and container queries within the `stylex.create()` definition. You can define responsive behavior by nesting `@media` rules inside your style objects, which the compiler transforms into proper CSS selectors while maintaining type safety.