DEV Community

Cover image for How to build a pricing slider - React
Pasquale Vitiello for Cruip

Posted on

8 2

How to build a pricing slider - React

In this tutorial, I am going to build a pricing component in React using the HTML structure from the previous article of this series.

Let's create a Pricing.js component file, and add the HTML into the return statement πŸ‘‡

import React from "react";

class Pricing extends React.Component {
  render() {
    return (
      <div className="pricing">
        <div className="pricing-slider center-content">
          <label className="form-slider">
            <span>How many users do you have?</span>
            <input type="range" />
          </label>
          <div className="pricing-slider-value">
            {/* Current slider value */}
          </div>
        </div>

        <div className="pricing-items">
          <div className="pricing-item">
            <div className="pricing-item-inner">
              <div className="pricing-item-content">
                <div className="pricing-item-header center-content">
                  <div class="pricing-item-title">Basic</div>
                  <div className="pricing-item-price">
                    <span className="pricing-item-price-currency" />
                    <span className="pricing-item-price-amount">Free</span>
                  </div>
                </div>
                <div className="pricing-item-features">
                  <ul className="pricing-item-features-list">
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li>Excepteur sint occaecat</li>
                    <li>Excepteur sint occaecat</li>
                  </ul>
                </div>
              </div>
              <div class="pricing-item-cta">
                <a class="button" href="http://cruip.com/">
                  Buy Now
                </a>
              </div>
            </div>
          </div>

          <div className="pricing-item">
            <div className="pricing-item-inner">
              <div className="pricing-item-content">
                <div className="pricing-item-header center-content">
                  <div class="pricing-item-title">Advanced</div>
                  <div className="pricing-item-price">
                    <span className="pricing-item-price-currency">$</span>
                    <span className="pricing-item-price-amount">13</span>
                    /m
                  </div>
                </div>
                <div className="pricing-item-features">
                  <ul className="pricing-item-features-list">
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                    <li className="is-checked">Excepteur sint occaecat</li>
                  </ul>
                </div>
              </div>
              <div class="pricing-item-cta">
                <a class="button" href="http://cruip.com/">
                  Buy Now
                </a>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

export default Pricing;

Shaping input and output data

We already have a data scheme with input and output values (check out the first article of this series to know more).

Key Slider value Price, currency Price, amount Price, after
0 1,000 Free
1 1,250 $ 13 /m
2 1,500 $ 17 /m
3 2,000 $ 21 /m
4 2,500 $ 25 /m
5 3,500 $ 42 /m
6 6,000 $ 58 /m
7 15,000 $ 117 /m
8 50,000 $ 208 /m
9 50,000+ Contact us

We will use the state object to contain that data.

Defining the component state

React has a built-in state object where to store property values that belongs to the component. I will use that to set input range and data scheme values.

state = {
  priceInputValue: "0", // initial input value
  priceInput: {         // slider values
    0: "1,000",
    1: "1,250",
    2: "1,500",
    3: "2,000",
    4: "2,500",
    5: "3,500",
    6: "6,000",
    7: "15,000",
    8: "50,000",
    9: "50,000+"
  },
  priceOutput: {        // output values
    plan1: {
      0: ["", "Free", ""],
      1: ["$", "13", "/m"],
      2: ["$", "17", "/m"],
      3: ["$", "21", "/m"],
      4: ["$", "25", "/m"],
      5: ["$", "42", "/m"],
      6: ["$", "58", "/m"],
      7: ["$", "117", "/m"],
      8: ["$", "208", "/m"],
      9: ["", "Contact Us", ""]
    }
  }
};

Now pass the priceInputValue to the input range defaultValue attribute:

<input type="range" defaultValue={this.state.priceInputValue} />

Setting range slider attributes

Create a ref and attach it to the input range element

// Create ref
slider = React.createRef();
// Attach ref
<input
  type="range"
  defaultValue={this.state.priceInputValue}
  ref={this.slider} />

Let's set the range slider min, max attributes, right after the component output has been rendered to the DOM πŸ‘‡

componentDidMount() {
  this.slider.current.setAttribute("min", 0);
  this.slider.current.setAttribute(
    "max",
    Object.keys(this.state.priceInput).length - 1
  );
}

We have now a range slider whose values go from 0 to 9! πŸ™Œ

Here is a recap of what we have built so far:

Binding input and output data

We have a working range slider, but it is still disconnected from the visualized price.

Before anything else, we need to update the priceInputValue property value every time a user interacts with the slider. To do that, let's create a method ...

handlePricingSlide = e => {
  this.setState({ priceInputValue: e.target.value });
};

... to be invoked when the input onChange event occurs.

<input
  type="range"
  ref={this.slider}
  defaultValue={this.state.priceInputValue}
  onChange={this.handlePricingSlide}
/>

OK, now we need another method to retrieve the data to be outputted, accordingly with the current input value

getPricingData = (obj, pos) => {
  return set !== undefined
    ? obj[this.state.priceInputValue][pos]
    : obj[this.state.priceInputValue];
};

This method has two parameters:

  • obj - The input or output object we want to retrieve data from
  • pos (optional) - The position of a required element in the array, if there is any. It is required for the plan objects, since to each key corresponds an array of values (e.g. 0: ["", "Free", ""], ...).

So, to output the current slider value, we will call the method like this πŸ‘‡

<div className="pricing-slider-value">
  {this.getPricingData(this.state.priceInput)}
</div>

And here is how to output the price data instead πŸ‘‡

<div className="pricing-item-price">
  <span className="pricing-item-price-currency">
    {this.getPricingData(this.state.priceOutput.plan1, 0)}
  </span>
  <span className="pricing-item-price-amount">
    {this.getPricingData(this.state.priceOutput.plan1, 1)}
  </span>
  {this.getPricingData(this.state.priceOutput.plan1, 2)}
</div>

Adjusting the slider value element position

Almost there. 🏁 We want the slider value to be following the slider thumb.

We need a new method for that but, before proceeding, there is another DOM element that needs to be referenced.

Create a ref and pass it to the current slider value

// Create ref
sliderValue = React.createRef();
// Attach ref
<div className="pricing-slider-value" ref={this.sliderValue}>

Get the slider thumb size from a CSS property πŸ‘

this.thumbSize = parseInt(
  window
    .getComputedStyle(this.sliderValue.current)
    .getPropertyValue("--thumb-size"),
  10
);

Now that we have a reference for the .pricing-slider-value element, we can create the method.

handleSliderValuePosition = input => {
  const multiplier = input.value / input.max;
  const thumbOffset = this.thumbSize * multiplier;
  const priceInputOffset =
    (this.thumbSize - this.sliderValue.current.clientWidth) / 2;
  this.sliderValue.current.style.left =
    input.clientWidth * multiplier - thumbOffset + priceInputOffset + "px";
};

Here is a visual representation of what the method does πŸ‘‡
Slider positioning adjustment
Call the method as soon as the component has rendered ...

componentDidMount() {
  this.handleSliderValuePosition(this.slider.current);
}

... and every time the input range value changes:

handlePricingSlide = e => {
  this.handleSliderValuePosition(e.target);
};

Conclusion

Here is the final result. Click on Open Sandbox to see the full code.

I hope you enjoyed this tutorial. If you want to see this in action, here is a landing page template where it’s implemented πŸ‘‰ Storm

Pricing component from Storm template

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post