Skip to content

Picking a Local Coding Model for TypeScript, React and CSS

SWE-bench measures Python bug-fixing. Here is what actually matters when picking a local model for TypeScript, React and CSS work.

· · 8 min read

Quick Take

Every ranking of local coding models leans on one benchmark, and that benchmark is 500 Python issues. If your day is TypeScript, JSX and a stylesheet that will not centre, the leaderboard is answering a question you did not ask. Here is a more useful way to choose, and the models that survive it.

TL;DR: The leaderboards rank Python bug-fixing. For TypeScript, React and CSS, pick on context window and tool-call reliability first, parameter count last. On a 24 GB machine ai/devstral-small-2 is the safe default; on 32 GB or more ai/qwen3-coder wins on context. And the 22% figure attached to Qwen3-Coder all over the internet belongs to a different model.

The benchmark everyone quotes is not measuring your job

Ask which local model is best at coding and you will be handed a SWE-bench Verified score. It is a good benchmark. It is 500 manually screened GitHub issues, and an agent passes only when a hidden test suite goes green, which is much harder to game than a function-completion test.

It is also entirely Python.

So when a 24B model scores 68% and you write React, you have learned something real but oblique: that model can navigate an unfamiliar repository, keep track of several files, and produce an edit that applies cleanly. Those skills transfer. What has not been measured is whether it knows that useEffect with a missing dependency array will re-run forever, whether it can write a discriminated union that actually narrows, or whether it reaches for flexbox when the answer is grid.

The benchmarks that would tell you are thin on exactly the models you can run at home:

BenchmarkCovers your stack?Local models listed
SWE-bench VerifiedNo, Python onlyMany
Multi-SWE-BenchYes, TS and JS includedSix models total
Aider PolyglotPartly, JavaScript not TS22, mostly frontier
WebDev ArenaYes, React and TS and TailwindFrontier-dominated

Multi-SWE-Bench is the right benchmark and almost nobody has been run through it. So the honest position is that for TypeScript specifically, you are choosing on proxies. The rest of this article is about picking better proxies than parameter count.

Fix the numbers first

Before comparing anything, one correction, because it has propagated into a dozen "best local model" posts.

You will repeatedly see Qwen3-Coder at 22% on SWE-bench Verified, presented as evidence it is far behind. That score belongs to Qwen3-30B-A3B, the general-purpose model. Qwen3-Coder-30B-A3B-Instruct, which is what docker model pull ai/qwen3-coder actually gives you, sits around 50 to 52% under standard scaffolding. The names differ by one word and the conclusion flips.

With that fixed, the honest scoreboard for the models Docker Hub ships in the ai/ namespace:

ModelSizeParamsSWE-bench VerifiedNative context
ai/devstral-small-215.2 GB24B dense~68% (vendor)256k
ai/glm-4.7-flash18.3 GB30B MoE, 3B active~59%~198k
ai/qwen3-coder18.6 GB30B MoE, 3B active~50-52%256k
ai/gpt-oss12.1 GB20B MoEnot published for 20B128k
ai/deepcoder-preview9.0 GB14B densenot comparable64k

Vendor-reported numbers are vendor-reported. Mistral's 68% for Devstral Small 2 is measured with their own scaffolding, which is a legitimate thing to publish and not the same as an independent run. Treat the column as a ranking hint, not a measurement.

The three things that actually decide it

Context window, and the memory it quietly costs

A React task is almost never one file. Change a component and you touch its props interface, the hook it calls, a test, and a stylesheet. An agent has to hold all of those, plus its own tool schemas, plus whatever it has already read.

This is where parameter count misleads. A 14B model with a 128k window will out-perform a 30B model pinned at 8k on any multi-file change, because the 30B one is working blind. Long native context is the single most predictive spec for agentic frontend work, and it is the one nobody puts in the headline.

The catch is memory. The weights are the number on Docker Hub; the KV cache is extra, it scales with the window you open, and on a 24 GB laptop an 18 GB model plus a wide context is how you discover what swapping feels like. Budget roughly:

  • 16 GB machine: 12 GB model, modest context. ai/gpt-oss, and note it ships a small default you will need to repackage.
  • 24 GB machine: 15 GB model with room to breathe. ai/devstral-small-2.
  • 32 GB and up: 18 GB model with a wide window. ai/qwen3-coder or ai/glm-4.7-flash.

Tool-call reliability, which is invisible until it isn't

Run any of these inside an agent and the failure you actually hit is not bad code. It is the model choosing the wrong tool, or passing the right tool an argument that makes no sense, and the loop grinding.

Worth knowing that under Docker Model Runner the syntax half of this problem is already solved for you: it compiles each tool's JSON Schema into a GBNF grammar and constrains the sampler, so malformed tool JSON is structurally impossible. What remains is judgement, and judgement is exactly what a 4-bit 20B model has least of.

Models fine-tuned for agentic use are markedly better here, and it has little to do with how much React they know. Devstral is explicitly trained for it. ai/qwen3-coder is built around tool use. A general-purpose model of the same size will write comparable JSX and pick the wrong action far more often.

This is also the axis where local models lose to hosted ones most clearly, and it is worth being blunt about it rather than discovering it three hours in.

Quantisation, the tax nobody mentions

Everything in the ai/ namespace runs through llama.cpp and arrives quantised. The size on Docker Hub is not the model, it is a compressed version of it, typically around 4 bits per weight.

For prose that is nearly free. For code it is not, and TypeScript is unusually exposed. Generic types, long identifier chains, and precise import paths are exactly the low-probability token sequences that quantisation degrades first. A 4-bit model that writes fluent-looking React can still hallucinate a member on a type or drop a generic parameter, and it will do it with total confidence.

The practical defence is not a bigger model. It is TypeScript strict mode plus a fast typecheck in the loop, so the compiler catches what the model got wrong before you read it. Local models make a good type system worth more, not less.

What I would actually pull

24 GB machine, general TypeScript and React work: ai/devstral-small-2. It is the best-benchmarked agentic model that fits with room left over, it does not need repackaging for context, and it fails less often on tool calls than anything else in its size class.

32 GB or more, large codebases: ai/qwen3-coder. The MoE architecture means only about 3B parameters are active per token, so it generates fast for its size, and 256k of native context is the thing that makes multi-file React refactors survivable.

16 GB machine: ai/gpt-oss, with realistic expectations. Single components, boilerplate, a stubborn CSS rule. Not agentic refactors.

Do not pull ai/qwen3 or ai/gemma4 for this. They are competent general models and they are not tuned for tool use, which is what the agent loop demands.

Test it yourself, in about fifteen minutes

Published benchmarks will not answer this for your stack, so run a small one. Five prompts, same for every model, graded by whether the output compiles and behaves:

  1. A typed useFetch<T> hook with loading, error and abort handling. Grades generics and cleanup.
  2. A discriminated union for a form state machine, plus an exhaustive switch. Grades type narrowing.
  3. A responsive card grid with CSS grid, no media queries, using minmax and auto-fit. Grades modern CSS versus 2018 CSS.
  4. A React component with a deliberately wrong useEffect dependency array, asked to be fixed and explained. Grades React-specific reasoning.
  5. A three-file change: rename a prop, update the type, update the consumer. Grades tool-call reliability, which is the one that actually separates them.

Task five is the tiebreaker. Tasks one to four most of these models pass. Task five is where you find out which one can be trusted inside an agent, and it is the reason a smaller well-tuned model often beats a larger clever one.

Getting any of them wired into Claude Code is covered in the Docker Hub setup, which also documents the grammar-compilation error that can block the whole thing before you evaluate a single model, and the cost arithmetic that argues against running local as your only model. If you would rather split the work than replace it, the hybrid routing setup puts whichever model you pick here behind the cheap half of every session. Once you have results, hold them to the same standard as anything else an AI writes for you, which is the point of our playbook for testing AI-generated components.

The honest summary

No local model that fits on a laptop is going to replace a hosted frontier model for React work in 2026. The gap is not knowledge of the framework, which they all have adequately. It is sustained agentic behaviour across a long task, and that gap is still large.

What has genuinely changed is the floor. A 15 GB download now writes correct typed hooks and modern CSS, offline, for free, on hardware you already own. Two years ago that was not true. Pick for context and tool reliability, keep the compiler strict, and treat the SWE-bench column as trivia.

Frequently Asked Questions

Does SWE-bench Verified tell me anything about TypeScript ability?
Indirectly at best. It is 500 human-screened GitHub issues from Python repositories, scored on whether a hidden test suite passes. It measures multi-file navigation and disciplined editing, which do transfer. It measures nothing about JSX, the TypeScript type system, or CSS layout, because none of those appear in it.
Is the widely quoted 22% score for Qwen3-Coder correct?
No, and it is worth correcting because it gets repeated everywhere. The 22% belongs to Qwen3-30B-A3B, the general-purpose model. Qwen3-Coder-30B-A3B-Instruct, which is the one Docker Hub ships as ai/qwen3-coder, lands around 50 to 52% under standard scaffolding. Two different models, similar names.
What is the minimum RAM for useful TypeScript work?
16 GB gets you a 12 GB model such as ai/gpt-oss with a short context, which is enough for single-file generation and not enough for agentic edits. 24 GB fits the 15 GB class comfortably. 32 GB is where an 18 GB model plus a 64k context stops being a compromise, because the KV cache needs room the weights do not.
Why does context window matter more than parameter count here?
Because a React change is rarely one file. The component, its props type, the hook it calls and the stylesheet are four files, and an agent has to hold all of them plus its own tool definitions. A 14B model with 128k of context is more useful on that task than a 30B model squeezed into 8k.
Can any of these replace a hosted model for React work?
For generating a component from a clear description, yes, several can. For a refactor spanning a dozen files where the model has to remember what it changed in file three while editing file eleven, no. That gap is about sustained agentic behaviour, not about knowing React.