🌶️ Currying React Components

UtkarshUtkarsh
4 min read

Introduction

If you’ve ever written repetitive or overly verbose React components and felt "there must be a cleaner way", then currying might just be your new best friend.

In this article, we’ll explore:

  • What currying is (in plain English)

  • How currying applies to React components

  • Why this pattern is powerful

  • Tons of examples (from basic to advanced)

  • Use-cases and gotchas

Let’s dive in! 💡

🧠 What is Currying?

In functional programming, currying is the process of transforming a function with multiple arguments into a sequence of functions, each taking one argument at a time.

Traditional Function

function greet(greeting, name) {
  return `${greeting}, ${name}`;
}
greet('Hello', 'Alice'); // "Hello, Alice"

Curried Function

const greet = (greeting) => (name) => `${greeting}, ${name}`;
greet('Hello')('Alice'); // "Hello, Alice"

Why is this useful? Because you can partially apply arguments and create specialized versions of functions easily.

🍱 Currying in React Components

Now let’s translate that concept to React.

Traditional Component

function Button({ variant, size, children }) {
  return <button className={`${variant} ${size}`}>{children}</button>;
}

You use it like:

<Button variant="primary" size="sm">Click Me</Button>

But what if you're always using variant="primary" throughout your app? Currying to the rescue.

🔁 Currying a Component

Step 1: Make it a curried function

const withVariant = (variant) => (props) => {
  return <Button {...props} variant={variant} />;
};

Now you can create a pre-configured component:

const PrimaryButton = withVariant('primary');

<PrimaryButton size="sm">Click Me</PrimaryButton>

✅ DRY (Don't Repeat Yourself)
✅ Easy to theme
✅ Improves readability

🔧 Real-World Examples

1. ✅ Pre-styled Button Variants

const withButtonVariant = (variant) => (size) => ({ children, ...rest }) => (
  <button className={`btn ${variant} ${size}`} {...rest}>
    {children}
  </button>
);

const PrimarySmallButton = withButtonVariant('primary')('sm');
const DangerLargeButton = withButtonVariant('danger')('lg');

<PrimarySmallButton>Save</PrimarySmallButton>
<DangerLargeButton>Delete</DangerLargeButton>

2. 🌍 Themed Components (like translation or directionality)

const withLang = (lang) => (Component) => (props) => {
  return <Component {...props} lang={lang} />;
};

const Greeting = ({ lang }) => (
  <p>{lang === 'en' ? 'Hello!' : '¡Hola!'}</p>
);

const EnglishGreeting = withLang('en')(Greeting);
const SpanishGreeting = withLang('es')(Greeting);

<EnglishGreeting />
<SpanishGreeting />

3. 🧱 Higher-Order Component Factory

Currying also makes it easier to compose HOCs in a readable way.

const withBorder = (color) => (Component) => (props) => (
  <div style={{ border: `2px solid ${color}` }}>
    <Component {...props} />
  </div>
);

const withPadding = (padding) => (Component) => (props) => (
  <div style={{ padding }}>
    <Component {...props} />
  </div>
);

const Title = ({ children }) => <h1>{children}</h1>;

const StyledTitle = withBorder('blue')(withPadding('20px')(Title));

<StyledTitle>Welcome</StyledTitle>

4. 🔁 Conditional Logic

You can even curry for conditional rendering:

const withAuth = (isAuthenticated) => (Component) => (props) =>
  isAuthenticated ? <Component {...props} /> : <p>Please log in</p>;

const Dashboard = () => <div>Welcome to the dashboard</div>;

const ProtectedDashboard = withAuth(true)(Dashboard);

🔄 Currying with Hooks

Currying isn’t just for components—it can work beautifully with hooks too.

const useFetcher = (baseURL) => (endpoint) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(`${baseURL}/${endpoint}`)
      .then((res) => res.json())
      .then(setData);
  }, [endpoint]);

  return data;
};

// Usage
const useGitHub = useFetcher('https://api.github.com');
const userData = useGitHub('users/octocat');

✨ Benefits of Currying React Components

  • Code Reuse: Create mini "component factories"

  • Clarity: Break down logic and props cleanly

  • Partial Application: Pre-configure components without wrappers

  • Testability: Smaller units are easier to test

  • Better Theming: Build dynamic design systems with less code

⚠️ Gotchas & When Not to Curry

  • Too much abstraction can make code harder to read for beginners.

  • ❌ Don’t curry everything—use it when it clearly improves DX (developer experience).

  • ⚠️ Be careful with props overwriting—always document what’s hardcoded via curry.

// This will override user's variant prop unintentionally
const CurriedButton = withVariant('primary');

// Be mindful of prop merging priority

🧪 Advanced Pattern: Currying with Composition Libraries

If you're using libraries like lodash/fp or ramda, currying becomes even more powerful.

import { compose } from 'ramda';

const enhance = compose(
  withBorder('green'),
  withPadding('10px'),
  withLang('en')
);

const EnhancedComponent = enhance(Title);

✅ Summary

Currying is a functional pattern that fits beautifully into React, especially when:

  • You want to pre-configure a component

  • You're building design systems

  • You’re composing HOCs or hooks

  • You love writing clean, declarative code

It’s one of those underrated tricks that, once mastered, makes you feel like a React wizard 🧙‍♂️.

0
Subscribe to my newsletter

Read articles from Utkarsh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Utkarsh
Utkarsh

I'm a MERN Stack developer and technical writer that loves to share his thoughts in words on latest trends and technologies. For queries and opportunities, I'm available at r.utkarsh.0010@gmail.com