# How to Override Astryx Component Styles with Tailwind, CSS Modules, or Plain CSS

> Learn to override Astryx component styles using Tailwind CSS, CSS Modules, or plain CSS. Leverage className, style, and xstyle props for seamless customization and guaranteed precedence.

- Repository: [Meta/astryx](https://github.com/facebook/astryx)
- Tags: how-to-guide
- Published: 2026-08-06

---

**Astryx components expose `className`, `style`, and `xstyle` props that append your custom styles after internal StyleX-generated classes, ensuring your Tailwind utilities, CSS modules, or inline styles always take precedence in the cascade.**

Astryx is Meta’s React component library built on StyleX for type-safe styling. While components ship with opinionated, token-driven defaults, the `facebook/astryx` source code implements a deliberate extension mechanism via the `mergeProps` utility that guarantees consumer overrides win without stripping base styles.

## How Astryx Handles Style Overrides

Every public component in Astryx accepts three styling props that feed into a centralized merging utility:

- **`className`** – Appends a string of CSS classes after the component’s StyleX-generated class. This is the primary hook for Tailwind utilities or CSS module classes.
- **`style`** – Merges an inline style object after the component’s internal StyleX style, ideal for dynamic values.
- **`xstyle`** – Accepts StyleX style objects (e.g., `stylex.props(customStyle)`) for advanced customizations using pseudo-classes or theme tokens.

The merging logic resides in [`packages/core/src/utils/mergeProps.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/utils/mergeProps.ts). This utility concatenates the consumer’s `className` after internal classes (`cls = "…${xdsClass} ${stylexResult.className} ${className}"`) and spreads the consumer’s `style` object after internal styles. Because your values are appended last, they naturally override defaults in the CSS cascade.

## Method 1: Tailwind CSS Overrides

Pass Tailwind utility classes to the `className` prop to override colors, spacing, or effects.

```tsx
import {Button} from '@astryxdesign/core';

export const SaveButton = () => (
  <Button
    label="Save"
    variant="primary"
    className="bg-green-600 hover:ring-2 hover:ring-green-300"
  />
);

```

**Why it works:** The `Button` component’s internal class is generated first, then your Tailwind string is appended. Tailwind’s later position in the cascade allows it to override the default background color when specificity is equal.

## Method 2: CSS Modules

Import scoped styles from a CSS module and pass the generated class string via `className`.

```css
/* MyButton.module.css */
.myButton {
  background-color: #ff6b6b;
  border-radius: 12px;
}

```

```tsx
/* MyButton.tsx */
import {Button} from '@astryxdesign/core';
import styles from './MyButton.module.css';

export const MyButton = () => (
  <Button
    label="Delete"
    variant="destructive"
    className={styles.myButton}
  />
);

```

**Why it works:** CSS Modules produce unique class names (e.g., `MyButton_myButton__abc123`). `mergeProps` appends this class after Astryx’s own classes, so your module’s declarations take precedence without bleeding into other components.

## Method 3: Inline Styles

Use the `style` prop for dynamic values that cannot be expressed with utility classes or when you need runtime calculations.

```tsx
import {Button} from '@astryxdesign/core';

export const WideButton = ({width}) => (
  <Button
    label="Full-width"
    variant="secondary"
    style={{ width, paddingBlock: '0.75rem' }}
  />
);

```

**Why it works:** Inline styles are merged after StyleX’s generated style object. Because the `style` prop is spread last in [`packages/core/src/utils/mergeProps.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/utils/mergeProps.ts), your values overwrite any conflicting internal declarations.

## Method 4: StyleX Style Objects (xstyle)

For projects also using StyleX, pass style objects directly to leverage theme tokens and pseudo-classes.

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

const custom = stylex.create({
  emphasis: {
    borderWidth: '2px',
    borderColor: 'var(--color-accent)',
  },
});

export const EmphasizedButton = () => (
  <Button
    label="Important"
    xstyle={stylex.props(custom.emphasis)}
  />
);

```

**Why it works:** The `xstyle` prop integrates with StyleX’s compiler, allowing you to reference design tokens defined in [`packages/core/src/theme/tokens.stylex.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/tokens.stylex.ts) while maintaining type safety.

## Understanding the Merge Order

When you combine multiple override methods, Astryx applies them in a predictable sequence. According to the implementation in [`packages/core/src/utils/mergeProps.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/utils/mergeProps.ts) and the component usage in [`packages/core/src/Button/Button.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.tsx) (lines 78-94), the final computed props follow this hierarchy:

1. **Internal StyleX classes and styles** – The component’s base appearance.
2. **`xstyle` classes** – StyleX-specific overrides.
3. **`className` string** – CSS modules, Tailwind, or plain classes.
4. **`style` object** – Inline styles with the highest precedence.

```tsx
import {Button} from '@astryxdesign/core';
import moduleStyles from './FancyButton.module.css';

export const FancyButton = ({isLoading}) => (
  <Button
    label="Submit"
    variant="primary"
    className={`
      ${moduleStyles.fancy}
      ${isLoading ? 'opacity-50' : ''}
    `}
    style={{ minWidth: isLoading ? '120px' : '80px' }}
  />
);

```

In this example, the cascade resolves as: **StyleX → CSS module → Tailwind → Inline**. Your custom styles always win because they are concatenated and spread last.

## Summary

- **Use `className`** for Tailwind utilities and CSS modules; values are appended after StyleX classes in [`packages/core/src/utils/mergeProps.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/utils/mergeProps.ts).
- **Use `style`** for dynamic inline values that require runtime calculations.
- **Use `xstyle`** when you need StyleX-specific features like theme tokens from [`packages/core/src/theme/tokens.stylex.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/tokens.stylex.ts).
- **Override precedence** is guaranteed by the merge order: internal styles first, consumer styles last.

## Frequently Asked Questions

### Does passing a className replace the default Astryx styles?

No. The component retains its internal StyleX-generated classes. Your `className` is appended after the internal class string, allowing your CSS to override specific properties through the cascade while preserving the component’s structural styles.

### Why do my Tailwind classes fail to override Astryx component styles?

This usually indicates a specificity conflict or purging issue. Ensure your Tailwind content configuration includes paths to Astryx component files so utilities are not tree-shaken. Additionally, verify you are using the standard `className` prop rather than attempting to style via wrapper elements, as Astryx components often merge classes directly onto the root element defined in [`packages/core/src/Button/Button.tsx`](https://github.com/facebook/astryx/blob/main/packages/core/src/Button/Button.tsx).

### Can I combine CSS Modules and Tailwind on the same component?

Yes. Both technologies output standard CSS class strings. Pass them together to the `className` prop: `className={\`\${styles.myModule} bg-blue-500 hover:bg-blue-600\`}`. The order in the string determines cascade precedence, with later classes overriding earlier ones when specificity is equal.

### How do I access Astryx theme tokens in my custom CSS?

Reference the CSS custom properties defined in [`packages/core/src/theme/tokens.stylex.ts`](https://github.com/facebook/astryx/blob/main/packages/core/src/theme/tokens.stylex.ts). For example, use `var(--color-primary)` or `var(--space-4)` in your CSS modules or global styles. These tokens are available at runtime because StyleX generates them as custom properties in the document root.