Every Project Needs This NEW Tanstack Library
Chapters7
The video introduces the Pacer library from Tanstack, focusing on debouncing, throttling, and rate limiting, and outlines demos showing how these patterns solve common web development problems.
TanStack Pacer simplifies debouncing, throttling, and batching in React with intuitive hooks like useDebouncedCallback and useThrottledValue.
Summary
Web Dev Simplified’s Kyle dives into TanStack’s Pacer library, showing how to tame rapid UI events with debouncing, throttling, and batching. He demonstrates a debounced search input using useDebouncedCallback, explaining how wait times (e.g., 1000 ms) prevent unnecessary API requests. The video then contrasts debouncing with throttling, using useThrottledValue and related hooks to limit updates during fast events like window resizing. Kyle also reveals how to access internal throttle state (isPending) and how to pass a third parameter to surface extra state for reactive UI cues. A practical autosave pattern showcases batch-based saves, leveraging useBatchedCallback to send data to the server only after a batch is ready or a timeout elapses. Throughout, he emphasizes the four core Pacer hooks (state, callback, value, and throttler) and how they can be combined to solve common webdev pain points. The takeaway is that TanStack Pacer offers ready-made, configurable solutions that dramatically reduce redundant requests and UI thrash in real-world apps. If you’re building responsive React apps that type, resize, or autosave, this library is worth integrating. Kyle also notes there are core variants for other frameworks beyond React, like core and spell versions. The video ends with a tease to explore more TanStack libraries like Start and AI in future videos.
Key Takeaways
- Using useDebouncedCallback with a 1000 ms wait delays handleSearch calls until typing pauses, reducing API requests.
- There are four Pacer hooks for debouncing/throttling: useDebouncedCallback, useDebouncer, useThrottledValue, and useThrottler, plus batching options.
- useThrottledValue returns a pair: an instantaneous value and a throttled value that updates at a set interval (e.g., every 1 second).
- The third parameter on throttling hooks exposes state like isPending, enabling UI cues like showing a pending indicator during throttling.
- Batching with useBatchedCallback lets you accumulate changes and emit a single server call (configurable wait and max batch size).
- Batching autosave patterns reduce server requests by grouping multiple edits into fewer network calls (e.g., five changes per batch).
- TanStack Pacer variants exist for frameworks beyond React (core, spell), making the approach portable across stacks.
Who Is This For?
Essential viewing for React developers who want robust, production-ready solutions for debouncing, throttling, and batching with TanStack. It’s especially helpful for those implementing live search, resize-heavy UIs, or autosave features.
Notable Quotes
"The most important one is the wait period. How long do you want to wait before we actually send a request?"
—Explains configuring debouncing with a wait time to control request frequency.
"Every time I type a letter into this input, I'm calling handleSearch. That means five search requests go off."
—Illustrates the problem debouncing solves in live search.
"When we use throttled state, we can only update the value once per second."
—Demonstrates throttling to limit updates during fast-changing events.
"You can access isPending from the throttler so you can show a pending indicator while waiting."
—Shows how to surface throttle state in the UI for better UX.
"Batching lets us save data to the server after a batch of changes or a timeout, reducing requests."
—Defines the autosave/batching concept with useBatchedCallback.
Questions This Video Answers
- How does TanStack Pacer debounced callback work in React?
- What is the difference between useDebouncedCallback and useDebouncer in TanStack Pacer?
- How can I implement batching with TanStack Pacer for autosave features in a React app?
- What are the four main TanStack Pacer hooks and when should I use each?
- Can TanStack Pacer be used with frameworks other than React?
TanStack PacerReact PaceruseDebouncedCallbackuseDebounceruseThrottledValueuseThrottleruseBatchedCallbackbatchingdebouncethrottle
Full Transcript
The Tanstack team has been creating tons of different new libraries, but one in particular I want to look at is the Pacer library. And this library is all about creating different debouncs, throttling, rate limiting, and so on. So you can see here we kind of have a quick demo of the differences between them. And in this video, I'm going to be going through a bunch of different demos that I've created to showcase not only how you can use this library in real world applications, but also more importantly, why you actually need this library and how it solves some really common web development problems.
[music] Welcome back to WebDev Simplified. My name is Kyle and to get started talking about this new Tanstack library, we first need to take a look at a sample piece of code that showcases the use case for this. In this simple little bit of code, I have a state in React for users. I have a loading state and then I just have a controller for cancelling my fetch request. You can see here I have a function called handle search. All I do is I cancel any existing searches. I come in here with my loading state and set it to loading.
I then fetch all of my users from a fetch request. And then finally, I just make sure to set that data. And if we go down to that fetch users function, you can see I'm just calling out to an API and using whatever query I pass into this search query up here as the thing that I'm searching for. So if I search, for example, for some stuff, you can see that I'm going to get back my results with the list of people that contain that data. And if I come up here and look, you can see anytime I change my input, I'm calling handle search.
Now, you may already see the problem here, and that's that every single time I type a letter into this input, I'm calling that handle search function. So, that means if someone types in five characters, there are five search requests that go off. And this means that you're going to be hitting your API a ton of different times with search requests, which is obviously not ideal. Instead, what we want to do is we want to debounce this. And that's essentially a technique to make sure that we don't send off too many requests. It essentially waits for us to stop typing in the input and then sends one request instead of sending a bunch of requests every single time we type in a character.
Now, we could go through and we could manually implement this debouncing, but this new tanstack library makes it incredibly simple and gives us a ton of different configuration. So, to use this library, all we need to do is just npmi tanstack/react-pacer. This is the React version of the library. You can obviously install like the core version or the spelt version or solid version, whatever you want, but we're working with React, so we're going to be using the React version specifically. Now, once you do that, it gives us access to a bunch of different hooks. For example, just for debouncing, we have access to four different hooks.
But the first one that I want to look at is the use debounced callback because that's probably the most common type of hook you're going to use is the one where you just want to get a call back because essentially all this does is it wraps a function and returns to you a new function that is debounced. Essentially, it'll only call after a certain delay. So, what we can do inside of here is we can pass it the function we want. Now, in my case, I'm just going to essentially take this entire handle search function.
I'm going to put it directly inside of here and just make sure I convert it into an arrow function. There we go. And now I've essentially created a debounced version of this function. We're going to make sure that it is async since I'm using asynchronous code inside of here. And that's going to give me back essentially my debounced search. There we go. And you can see we get an error with this because it expects me to pass two arguments. So the second argument I need to pass in is all of my options for configuration. And the most important one is the wait period.
How long do you want to wait before we actually send a request? So that means that we need to stop calling this function for a set period of time before it actually executes the code inside of it. So if we say a thousand milliseconds, that means that this function every time that we call it is going to wait one whole second. And if we call that function in that 1 second wait period, it's going to restart that timer until eventually we don't call the function for a whole second and then it actually executes. So if I replace handle search with my debounce search down here, all the rest of my code stays the same.
My function stays the same. All I did is wrap it inside this hook right here. You can now see when I'm typing, there's no loading state. It's just letting me type just fine. And as soon as I stopped for 1 second, then it goes through and actually makes my fetch request. So, in this case, we typed a bunch of times, but it only sent across one fetch request. Now, this is probably the most common scenario you're going to run into. And this is why I recommend using this callback version because it just gives you the function back and you can use it.
But if you need to get even more information about the debouncer, you can just use the use debouncer hook directly. And instead of giving you a function, that's going to give you some debouncer object. And this debouncer object has a bunch of different properties on it. The main one is maybe execute, which is essentially calling the function. And then we have a lot of instances for being able to cancel things, flush things out, and so on. For the most part, though, you're probably just wanting to call the function. So, it really doesn't make sense to use this use debouncer hook.
It's more likely that you're going to be using this use callback version or one of the other versions that are available as well, which is what I'm going to be covering in the very next example I want to talk about. Now, this new example of code is relatively straightforward. All that we have is a simple use effect here that every time my window resizes, I'm setting this width variable right here to that specific value and then I'm printing that out on my screen. So you can see as I change the value of my screen size by resizing it, you can see that width variable also changes right there.
Now that may have been a little bit hard to see because things were moving around, but you can see as I resize my screen browser by just scrolling from the right hand side instead of the left, you can see that that number is constantly updating. Now in our case, let's say that we were doing a really expensive calculation. every single time our window size changed, we were doing a bunch of stuff inside of our code to move around elements or do like fetch request or whatever it is. We don't want to call that thousands of times because if we resize our screen a bunch, it's going to call that hundreds or thousands of times.
So instead, we want to essentially delay this and we're going to be using a throttle in this case. A throttle essentially prevents something from executing more than one time every set period. So what we can do here is we can say we want to use a throttled. In our case, we're going to be using throttled state. This works just like a normal state setter except for instead of updating the value immediately, it essentially prevents us from updating the value more than one time period we specify. In our case, we're going to specify a weight period here of 1 second.
So essentially, we can only update this value one time per second. Now, if we come and we actually look at what our screen looks like and we change around our width, you'll notice that the actual value for my width is only changing once every single second. Now, in a real world scenario, you'll probably reduce this to maybe 100 milliseconds or even 50 milliseconds, so that it seems relatively quick. You can see here at 50 milliseconds, it's still quite quick, but it's not spamming you nearly as much as before. And something like maybe 100 milliseconds is probably a good sweet spot.
So 10 times per second, it'll update, but it prevents us from being called hundreds and hundreds of times. And another really important thing to note about this entire tan stack library is if we look at this use throttled, you can see there's four options. There's use throttled state, use throttled callback value, we have use throttled value, and then we just have the actual use throttler as well. Those are our fa four main instances we can use. And every single hook inside this, every single thing like batching, queuing, debouncing, throttling, all of those have these four different hooks essentially that you can use.
You can get the callback version, you can get the state version, which we've already looked at. You can get the throttler. And then finally, you can get the actual value itself, which is something that we're going to be looking at next. This use throttled value is really useful for when you specifically want to have an instantaneously updating value and you want to have a delayed updating value. So, I'm actually going to take all my code. I'm going to change it back to what we originally had with our normal state and everything like this. And instead, I want to be able to keep track of both my state as well as my throttled state as well.
So, I'm just going to come down here. I'm going to copy this. We're going to get our throttled width just like that. I'm going to create a brand new set of variables. We could say throttled width just like that. And this is going to be equal to that use throttled value. There we go. And we're going to set this value to the value we want it to update to. Just like that. We'll set the width as the value we want this to update to. And we'll pass in our throttled width down here. And most importantly, we need to pass along our weight parameter.
So we'll just say every 100 milliseconds. Or let's make it every second. So it's very clear what's happening. So essentially the way that this code works is we're updating this width variable instantaneously. You can see as I resize my screen that top number updates instantaneously. This bottom number here though for our throttled value, this just takes whatever the current value is and every single 1 second since we're using throttling, it'll update to the most recent value. So it only updates at most one time per second. Now you will notice it actually returns to me an array of values.
I'll get to that in a little bit, but otherwise the rest of our code is exactly the same. So we have one instantaneously updating value and we have one value that updates essentially at a 1se second interval, which is perfect for exactly the use case that we want to have here. And again, depending on what you want, you may want to want to use one of these over the other. But the important thing to note is that it doesn't really matter which one you use because they all can kind of give you the exact same result.
Now, this thing returning an array value here is because actually all of these different values, the use throttled value, use throttled state, they return to you the just underlined throttle throttler just like that. So this is that same thing we saw with the debouncer. So if I were to just call that throttler dot, you can see again we get all the exact same parameter stuff that we got when we're using the demouncer. We get the same exact thing when we're using a throttler. So if you're using the use throttled value or use throttled state, you get access to this throttler if you actually want it.
The only time you don't get access to that is if you use the callback version cuz that only returns to you a function with no additional parameters. Now throttling is a really great use case when you have things that are changing quickly such as resizing or scrolling. Anything related to that, you're going to want to throttle. Another really important thing that you can do with all of these different hooks is you can pass along an additional third parameter. And this third parameter is for getting all of the state information that comes from the throttler itself.
This takes in a function. And this function is going to return an object. And this object is all the state things that you care about. For example, we can get the is pending state. This is essentially while we're waiting for our throttle to be done executing. So like we called the function and now we're trying to wait for the next throttle to be available. That's what this is pending is for. And we can get that from our state. You can see it has access to quite a lot of information. We'll just say state.ispending. And anything that we return from this function right here is going to be automatically rerender in our component when it changes.
So when this is pending value changes, it's going to rerender our component. And we can get access to that from that throttler itself because this has access to all of our state information. So I can actually just use that down here. I'm just going to put it inside of an H3 so we can actually see what's going on. And I'll say that we want to get our throttle and we can get the state and we want to get that is pending variable that we added onto it. And if we have a pending state, then we'll say pending.
Otherwise, we'll say done or something like that. It doesn't really matter. Just so you can see the text right here is done. And as I'm actually resizing my screen, you can see while it is waiting for an update to happen, it's in that like 1 second wait period. You can see it says pending. So we are able to query information directly from the state stored inside a tan stack by using this third parameter and just passing it along all the different state stuff we care about actually getting back and rerendering our component when it changes cuz without this our component won't rerender when this value changes.
Now I can actually showcase that to you because if you look here now when I'm resizing my screen this done variable just always stays done because it's not being reactively rendered and that's again because of the way React works. So if you want to make sure that that value is updated inside your component, just make sure you pass along this third parameter right here. Now this next example I want to talk about is a perfect use case for when you want to batch together multiple things to occur at once and that is like an autosave feature.
So the whole idea behind this example is imagine something like Figma or Google Docs where as you make changes it automatically saves those changes for you. And the way that those systems work is behind the scenes they batch together all your changes and send them all at once to their server. This current implementation doesn't do that though. The way that it's working is we're storing two variables, a count variable, and then we're storing this server count. Now, this is essentially a placeholder for the data that is stored on your server specifically. So, this should only update when we update the data on our server.
Right now, every single time our count changes, we're saving that data to our server. And all this does is it just waits 1 second and returns our count. That is the new count for our server. Imagine this makes a fetch request and stores data inside your database. So, every single time our count changes, we're updating that data on our server. When I click this button, you'll notice that 1 second later, it continually updates on my server. And you'll notice it updates one time for every time I click my account. So, every single time I make a change, it sends a request to my server.
For example, if Google Docs sent a request to Google saying that they wanted to save your document, every single time you changed a single letter in your Word document, it would take down the entirety of Google cuz that's way too many requests. So, instead, what they do is they batch those requests together. They say, "Hey, once you send along 10 different requests, we're going to do that and send it all as one single change to our server to save us the bandwidth." So we have onetenth the amount of request going to our server. This is the idea of batching.
And this is actually really easy to do in Tanstack with their batchbased queries. So we can come in here with a used batch and we can get the used batched callback. So our use batched callback is going to take in a function and this is the function that's going to do the code we want. So it's all this code in this use effect right here. This is going to save that information to our server. Then we need to specify our wait period and this is how long that we want to wait if there are no changes.
So like let's say that we wanted to do every 10 requests we send one request to our server. But if the user only sends five request well obviously we have a problem. So this weight essentially says okay even if they haven't sent a full 10 request after a certain period of time we're just going to send the data anyway. In our case we'll just put this to 2 seconds. Then we can come in here and say our max size. This is how big we want our data to be. In our case I'm going to set this to five.
So now essentially the way that this code is working is it's saving my data to the server only after I have at least five things to save inside my server. So it's only saving my new state after I update five specific times again to reduce the amount of queries that happen by five. And if I wait 2 seconds and I don't have a full query of five, it'll just send along what I am currently at. Then inside of my use effect, I'm just going to do a quick batch save right here. So we'll come in here with a batched save just like that.
And we'll pass along our count. and we'll come in here and say that this is our batched save. Just like that. So that's all the code that we need to do. Let's just make sure we put our batched save in our dependency array, even though it technically never changes. And that right there should be everything we need to do. Now, if we come in here and we click our count, you can see that after a delay, it is only updating every single five times that we change it. And then you can see here after a 2-cond delay, it finally updated to 17 because I didn't have a full batch of five.
If I do a bunch more, you'll notice again it did it in batches of five. and then after a two second delay, it'll do that final one. So, this is a great way for me to reduce the amount of times that I call out to my server because I'm now only calling out every five times I update my state instead of every single time. And you could obviously increase this even further. You can make it 20, for example, and make this a 10-second wait period. And now you're reducing the actual amount of data that's being sent to your server by 20 times, which is incredible.
Now, as I mentioned at the start of this video, there are tons of different Tanstack libraries come out. And if you want to learn more about Tanstack Start and Tanstack AI, which are some of their newest libraries, I'm going to link videos on those right over here.
More from Web Dev Simplified
Get daily recaps from
Web Dev Simplified
AI-powered summaries delivered to your inbox. Save hours every week while staying fully informed.





