The small frontend details that make an app feel finished
The difference between an app that feels rough and one that feels solid is usually a handful of small things nobody notices when they are done right. Here are the ones I always check.

You can build an app that does everything it is supposed to and still have it feel rough. Everything works, but it feels a little cheap, a little unfinished, and users pick up on it even if they cannot say why. The difference is almost never one big thing. It is a handful of small details that nobody notices when they are done right, and everybody feels when they are missing.
These are the ones I check on everything I build.
Tell people something is happening
The moment a user clicks a button that talks to a server, there is a gap before anything comes back. If nothing changes on screen during that gap, it feels broken. So they click again. Now you have two requests and a confused user.
The fix is simple: the instant they click, show that you heard them. Disable the button, show a small spinner, change the label to "Saving." It does not make anything faster, but it makes the wait feel handled instead of frozen. This one detail removes a surprising amount of frustration.
Design the empty state on purpose
Every list starts empty. The first time a user opens the app, before they have added anything, they see the emptiest version of it. Too often that screen is just blank, which reads as broken.
An empty list is a chance, not a gap. A short line explaining what goes here and a button to add the first thing turns a dead end into a friendly starting point. It is one of the cheapest things you can do and one of the most noticeable.
Say what went wrong in plain words
Things fail. The network drops, the server is busy, something times out. When that happens, "Error" or a raw technical message tells the user nothing except that they are stuck.
Tell them what happened and what to do about it, in normal words. "We could not save that, check your connection and try again" respects the person on the other end. And whenever you can, do not throw away what they typed. Making someone refill a form because your save failed is the fastest way to lose them.
Let the keyboard do its job
Not everyone uses a mouse. Some people navigate with the keyboard by choice, some out of necessity. If you can tab through a form in a sensible order, submit it with Enter, and always see where you are, the app works for far more people. If you cannot, it quietly shuts some of them out.
You get most of this for free by using real buttons and real form fields instead of gluing click handlers onto plain elements. The browser already knows how these should behave. Let it.
Respect people who ask for less motion
Animations can make an app feel nice. For some people they cause real discomfort. Browsers let a person say they prefer less motion, and it takes almost nothing to listen: keep the animation for everyone else, and for them, just skip to the result. It is a small kindness that costs you a couple of lines.
None of this shows up on a feature list
You will never see "the buttons show a spinner" or "the empty states are thought through" written on a spec. That is exactly why they get skipped, and exactly why doing them makes an app feel a class above one that did not bother. The features are what the app does. These details are how it feels to use, and that is the part people actually remember.