Sign in

Colum Kelly

@columk.bsky.social
38 followers 169 following 91 posts

Fullstack Developer | Climbing | Paragliding

PostsRepliesMedia
Colum Kelly @columk.bsky.social · 08/10/2026
I was going to make the counter argument that throttled Lighthouse scores aren't at all representative of the users of these products but the X example is actually bonkers, even on CrUX. Perf has degraded almost every single week since May for real users (by up to 500ms per week).
Chart showing performance regressions of x.ai's homepage on CrUX Vis.
000
Colum Kelly @columk.bsky.social · 19/09/2026
Good list but I can't make out the examples on my phone due to the low contrast. We're supposed to see subtle border radius differences in the first example but the contrast ratio is too low. There's even text with a ratio of 1.22 @jakubkrehel.bsky.social interfaces.dev/cheat-sheet
interfaces.dev
Interfaces › Cheat Sheet
Practical tips for making your interfaces clearer, easier to use and more polished.
000
Colum Kelly @columk.bsky.social · 18/09/2026
The tatem.com landing page has a full screen announcing the fact that they have a dark mode toggle. Maybe it's something to do with my scroll wheel but these kinds of sites tend to drive me into a rage. Does anyone enjoy this? #webdev #web #frontend
000
Colum Kelly @columk.bsky.social · 18/09/2026
I rolled my eyes when I saw the theme toggle article going around but the follow up is decent. I've always thought of it as something fun to have on a personal website. Otherwise it's a set-and-forget setting that doesn't need to be visible, and only needed in the kind of apps you spend hours on.
000
Colum Kelly @columk.bsky.social · 09/03/2026
Something like this: } catch (_) { if (callIdRef.current === callId) { setLikeState(prev => ({...prev, optimisticLiked: prev.serverLiked})); } }
000
Colum Kelly @columk.bsky.social · 09/03/2026
Yes, there are still bugs in this version, but your solution doesn't solve the issue. Optimistic state will still revert to the stale server state in the catch block. You would need to introduce a new ref for serverState, or combine both states into an object so you can use its updater function.
100
Colum Kelly @columk.bsky.social · 09/03/2026
Really cool! How do you feel about the 500ms? Normally that would provoke me but with the amount of content it might be okay in this case. I usually find 300ms to be the max tolerable duration for this type of animation, and even less for things like sidebars/drawers that are repeatedly triggered.
100
Colum Kelly @columk.bsky.social · 19/02/2026
They've been rewriting these docs recently and it's a big improvement. When I started thinking of useActionState as "useAsyncState" or even "useAsyncReducer' it unlocked it for me. It's really for any state value that is updated async.
000
Colum Kelly @columk.bsky.social · 18/01/2026
I don't think it would make sense to link to an edited down version of the talk, given the context. It's also missing the part I quoted from.
110
Colum Kelly @columk.bsky.social · 17/01/2026
Yes, I meant the call to set the optimistic state shouldn't be wrapped in another one (a nested transition), like you would with the useState updater. I thought that could be what you meant by "everything should be in a transition except useOptimistic".
120
Colum Kelly @columk.bsky.social · 16/01/2026
That's not my take so you'd have to ask Ricky. Text inputs should be the highest priority, but if the input is causing some side effect then that should be deferred in some way. Either with a transition or by using useDeferredValue to use the input value elsewhere with a lower priority.
120
Colum Kelly @columk.bsky.social · 16/01/2026
I think he means that the call to the optimistic updater itself doesn't have to be wrapped in a transition, since it's going to break out of the transition anyway to perform the update.
210
Colum Kelly @columk.bsky.social · 16/01/2026
www.columkelly.com/blog/use-opt... Updated the todo examples and some of the writing after getting feedback from @ricky.fm To be fair, Ricky's version of the final example is very clean, (although it took me a while to grasp). useOptimistic might save you after all.
columkelly.com
useOptimistic Won't Save You | Colum Kelly
Thoughts on the useOptimistic hook that was introduced in React 19
182
Colum Kelly @columk.bsky.social · 16/01/2026
That one should work, even when mashed.
000
Colum Kelly @columk.bsky.social · 16/01/2026
🤣 (if you're in a transition!) I'll update the todo examples tonight.
110
Colum Kelly @columk.bsky.social · 15/01/2026
Wow that's really clean, thanks. Useful to think of it that way. useAsyncState.
120
Colum Kelly @columk.bsky.social · 15/01/2026
New blog post www.columkelly.com/blog/use-opt... #webdev #react
columkelly.com
useOptimistic Won't Save You | Colum Kelly
Thoughts on the useOptimistic hook that was introduced in React 19
220
Colum Kelly @columk.bsky.social · 14/01/2026
Tech blogs that I used to enjoy are entirely written by ChatGPT now. I get it, we all want more free time, but it's still a shame. sunilpai.dev/posts/seven-...
sunilpai.dev
where good ideas come from (for coding agents)
(and the part where users have to level up)
000
Colum Kelly @columk.bsky.social · 05/12/2025
@emilkowal.ski I'm very curious what you think of the async react showcase at async-react.dev The intention was to demo the last 10 years of innovation but it seems like bad UX to me. I know you have thoughts on why loading patterns like this can make an app feel much slower than it is.
async-react.dev
Async React
000
Colum Kelly @columk.bsky.social · 31/10/2025
Hear hear tanstack.com/blog/directi...
tanstack.com
Directives and the Platform Boundary | TanStack Blog
A Quiet Trend in the JavaScript Ecosystem For years, JavaScript has had exactly one meaningful directive, "use strict". It is standardized, enforced by runtimes, and behaves the same in every environm...
000
Colum Kelly @columk.bsky.social · 30/10/2025
Thanks @deadcode.website!
000
Colum Kelly @columk.bsky.social · 19/10/2025
This is really cool blog.cloudflare.com/capnweb-java... #webdev
let user = api.authenticate(token);

// Get the user's list of friends (an array).
let friendsPromise = user.listFriends();

// Do a .map() to annotate each friend record with their photo.
// This operates on the *promise* for the friends list, so does not
// add a round trip.
// (wait WHAT!?!?)
let friendsWithPhotos = friendsPromise.map(friend => {
  return {friend, photo: api.getUserPhoto(friend.id))};
}

// Await the friends list with attached photos -- one round trip!
let results = await friendsWithPhotos;
020
Colum Kelly @columk.bsky.social · 15/05/2025
😜 alex.party/posts/2025-0... #webdev #ai #frontend
alex.party
The future of web development is AI. Get on or get left behind.
Editor’s Note: previous titles for this article have been added here for posterity.
010
Colum Kelly @columk.bsky.social · 15/04/2025
Seconding that the scroll is pretty finicky on mobile. Tricky to get the tab content centered and then the tabs sometimes won't scroll until they're focussed first with a tap.
110
Colum Kelly @columk.bsky.social · 13/04/2025
My Discover page is all cute pets and nature photos no matter how many times I select "see less of this" on those posts. Something is definitely broken.
080
Colum Kelly @columk.bsky.social · 12/04/2025
With image models you can train a LORA to reproduce a conventionally beautiful face accurately using as little as one reference image. With an unusual face, you need to train with a much larger dataset to get the same result.
000
Colum Kelly @columk.bsky.social · 10/04/2025
I've been playing around with this. I'd like to have a component continue suspending until the image within has fully loaded. I have tried throwing a promise and then resolving it in the onload callback but couldn't get it to work.
000
Colum Kelly @columk.bsky.social · 08/04/2025
People conflate SPA with CSR and MPA with SSR, forgetting that SPAs can combine rendering methods. I'd say most RSC apps are SPAs, although an RSC app with form navigation is actually a combination of SPA and MPA.
191
Colum Kelly @columk.bsky.social · 08/04/2025
I see Kafka in a lot of job requirements so this was useful context. Following up on your question to listeners, I'd love to hear a deep dive on long polling. Or even a full podcast on different ways to push data to the client. Long polling, SSE, websockets...
120
Colum Kelly @columk.bsky.social · 31/03/2025
I've made the mistake of listing out a bunch of input attributes as prop types when I want to pass them to a component. React actually a type for these which makes it much cleaner. #webdev #frontend #react #typescript
type InputProps 
  = InputHTMLAttributes<HTMLInputElement> & CustomInputProps

const Input = ({customProp, ...rest}: InputProps) => {
  console.log(customProp)
  return <input {...rest} />
}

export default function App() {
  return <Input type="text" name="foo" customProp="bar" />
}
010
Colum Kelly @columk.bsky.social · 26/03/2025
Really enjoyed your talk on API design yesterday. It's here if anyone's interested: youtu.be/l3PxErcKeAI?...
youtu.be
React Query API Design: Lessons Learned - Dominik Dorfmeister
YouTube video by BeJS
010
Colum Kelly @columk.bsky.social · 26/03/2025
@wesbos.com thanks, that was my potluck q about NextJS. I'm still confused because revalidateTag also clears the client's router cache completely. In my testing, even calling it on a tag that doesn't exist does this. It makes all layouts and routes that were cached suspend again when navigating.
010
Colum Kelly @columk.bsky.social · 25/03/2025
@tej.as do you really need to rely on data-test attributes with Playwright? I thought the standard was to use user-facing attributes like accessibility roles and labels.
000
Colum Kelly @columk.bsky.social · 25/03/2025
A great interactive visualisation of different types of storage from PlanetScale. Reminds me of Sam Rose's Turing Machine visualization. planetscale.com/blog/io-devi... #webdev
planetscale.com
IO devices and latency — PlanetScale
Take an interactive journey through the history of IO devices, and learn how IO device latency affects performance.
000
Colum Kelly @columk.bsky.social · 20/03/2025
There's a node compat flag that adds unjs polyfills for a lot of them, but not http. Workers use web requests. Best bet is to nag the library authors to use web APIs.
000
Colum Kelly @columk.bsky.social · 13/03/2025
I think it might be working because I hydrate the store in an effect with data from the server. It seems to be working anyway. I'm uncertain as the docs are clear that using it outside of context will cause issues. In case you're curious: github.com/columk1/thre... github.com/columk1/thre...
000
Colum Kelly @columk.bsky.social · 13/03/2025
@tkdodo.eu is there a way to force a global state issue with Zustand on the server? I'm using it in Next.js without Context but I'm struggling to find any issue. I've tried running multiple browser sessions and a suite of e2e tests but everything seems to work fine, no state is shared between users.
110
Colum Kelly @columk.bsky.social · 13/03/2025
There are lots of posts with 1 like and no replies, and sometimes they're above posts that were made more recently with more interaction. It's all a bit confusing. None of it fits the "trending" descriptor.
030
Colum Kelly @columk.bsky.social · 12/03/2025
Finished my Threads Clone. Next 15, React 19, Drizzle, SQLite, custom auth. I nabbed the rolling counter from Bluesky and a few ShadCN components, but otherwise everything was done from scratch. @syntax.fm you guys got me here! Someone hire me I'm sick of cleaning windows 😅 #webdev #react #nextjs
010
Colum Kelly @columk.bsky.social · 12/03/2025
This is a handy little tool for checking how OG images show up on different sites during local development. chromewebstore.google.com/detail/kckjj... #webdev #frontend #seo
chromewebstore.google.com
Localhost Open Graph Debugger - Chrome Web Store
Localhost Open Graph Debugger is a Chrome extension designed to check the Open Graph meta tags on locally hosted websites.
033
Colum Kelly @columk.bsky.social · 21/02/2025
Those considering server components should be thinking about bundle size vs. payload size. If most of the app is behind a login, will a slightly smaller bundle make a difference? If first-time visits are a priority (news, e-commerce), server components could be beneficial. #react #webdev #nextjs
010
Colum Kelly @columk.bsky.social · 11/02/2025
It would be awful if someone without :has support got stuck with the loading screen, but you could have a callback ref that animates it out no matter what after a set timeout.
000
Colum Kelly @columk.bsky.social · 10/02/2025
But if you put your fallback outside the suspense boundary. You can listen for that promise with an attribute like data-loaded in the content. This will appear when the promise on the server resolves. Then you can trigger an exit animation on the fallback using :has and unmount with onTransitionEnd.
000
Colum Kelly @columk.bsky.social · 10/02/2025
Found a powerful use for CSS :has with server components. This would fit in @joshwcomeau.com's "Global detection" category. When a component is within a suspense boundary, it's very difficult to animate out of the fallback, since it's unmounted whenever the promise used by the component resolves...
210
Colum Kelly @columk.bsky.social · 06/02/2025
Any chance you've found a way to remove the scrollbar thumb while scroll is locked in Safari? It's simple in Chrome but there seems to be an undocumented rule in Safari that prevents changes to the ::webkit-scrollbar rules being picked up after initial page load.
010
Colum Kelly @columk.bsky.social · 06/02/2025
The Node Modules Inspector looks beautiful.
040
Colum Kelly @columk.bsky.social · 04/02/2025
But it's still risky to trust either of them in this case. Just like || won't give you the fallback for 0, using ?? won't give you the fallback for values like NaN or "". Neither cover all cases so it's better to use type checks or to enforce the types at another level.
010
Colum Kelly @columk.bsky.social · 01/02/2025
This component wraps a card, allowing it to be clicked anywhere, without compromising the ability to select text. handleNestedInteraction opens the link (navigate) as long as the event target isn't a nested button. #webdev #frontend #react #javascript
<div
  role="link"
  onPointerDown={() => {
    window.getSelection()?.removeAllRanges()
  }}
  onClick={(e) => {
    // Don't navigate if text is being selected
    if (window.getSelection()?.toString()) {
      return
    }
    handleNestedInteraction(e, navigate)
  }}
  onKeyDown={(e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      handleNestedInteraction(e, navigate)
    }
  }}
  tabIndex={0}
  className="link cursor-pointer"

  {children}
</div>
010
Colum Kelly @columk.bsky.social · 01/02/2025
Love this guy! He does Burial too. My brain has a hard time believing where the sound is coming from.
010
Colum Kelly @columk.bsky.social · 31/01/2025
Okay I updated and now it's showing the option. They must have had earlier versions without it. Not sure why I missed the update, it's set to automatic.
010