A library is code you call when you need it. A framework is code that calls you: it sets the structure of your app, and you fill in the parts it leaves open. Which one you pick decides who is in charge of your code, and that affects how you organise it and how hard it is to change later.
A library is a tool you pick up
A library is a problem someone else has already solved, packaged so you can reuse it. Dates are the classic case. Formatting a date sounds simple until you meet time zones, daylight saving changes, months of different lengths and leap years. Get one of those wrong and a day goes missing from a booking calendar.
So instead of writing all that yourself, you install a date library, import the functions you need and call them:
import { addDays, format } from 'date-fns';
const start = new Date(2026, 0, 30);
const due = addDays(start, 3);
console.log(format(due, 'd MMM yyyy'));
// 2 Feb 2026The library did the hard part (rolling over from January into February) and handed back a value. Then your code carried on.
That is the shape of every library. Your code stays in charge. It decides when to call the library, whether to call it at all, and what to do with the result. Python's requests for HTTP calls, NumPy for maths on arrays and lodash for working with JavaScript collections all work the same way. If a better library comes along, you swap out the calls and nothing else in your app has to change.
A framework is a blueprint you build inside
A framework works the other way round. It is a blueprint for a whole kind of app, and it has opinions about:
- where files live: which folder holds your pages, models or tests;
- how data flows: how a request comes in, gets routed and turns into a response;
- how features are built: what a page, a route or a database model looks like.
You still write the code that makes your app yours. But you write it in the places the framework expects, in the shape it expects, and the framework runs it at the right moment.
Next.js is a good example, because its opinion about where files live is also its routing. In its App Router, a file called page.tsx inside a folder becomes a page at that folder's path:
app/
page.tsx -> /
cats/
page.tsx -> /catsThe page itself is a plain function:
// app/cats/page.tsx
export default function CatsPage() {
return <h1>All the cats</h1>;
}Nothing in your code ever calls CatsPage. Next.js finds it because of where the file sits, and calls it when someone visits /cats. Move the file and the URL changes. That is what 'following the framework's way' means in practice.
The real difference: who calls whom
What separates the two is the direction of control:
- With a library, your code calls the library.
- With a framework, the framework calls your code.
This flip has a name: inversion of control, sometimes called the Hollywood principle, 'don't call us, we'll call you'. The framework owns the main loop of the program. It starts the server, receives each request, picks the route, runs your handler, renders the result and deals with errors. You supply the pieces it plugs in: route handlers, components, models, settings.
Many frameworks take this one step further and create your objects for you, handing each one the things it depends on. That is dependency injection, a close relative of the same idea.
Size is a poor guide to which is which, since some libraries are far bigger than some frameworks. A better test is to imagine deleting the tool from your project. Remove a library and you rewrite the handful of lines that called it. Remove a framework and the structure of the whole app goes with it.
One app, two approaches
Say you are building a small booking site: a few pages, a form, a database and a confirmation email.
Built from libraries
You choose an HTTP server, a router, a template engine, a form validation library, a database client and a date library. Then you decide how they fit together: the folder layout, where the database connection is set up, how errors reach the user. You have full control, and nothing in the app is there unless you put it there. The cost is that every one of those decisions is yours to make, document and keep consistent. Two developers on the same team can easily end up structuring things two different ways.
Built on a framework
With a full framework such as Django, most of those decisions are already made. You get a project layout, a URL configuration, an ORM for the database, forms with validation, templates and an admin site. You write the models, the views and the templates, in the places Django expects them. A new developer who knows Django knows where to look on day one.
Most apps use both
A Django app still installs libraries for specific jobs, such as a date library, a payments client or an image resizer. The framework gives the app its shape, and libraries fill in particular tasks inside it.
Is React a library or a framework?
React is the usual grey area. It describes itself as a library for web and native user interfaces. It handles one job, turning components into what you see on screen, and leaves routing, data loading and folder structure to you. But React calls your component functions itself, whenever it needs to draw them, so it already has some inversion of control.
Frameworks built on top of it, such as Next.js, add the missing opinions: routing, data loading, a project structure and a build setup. Angular sits at the far end of the scale, a full framework that makes most of those decisions itself.
The label matters less than the question behind it: how much of my app's structure does this tool decide for me?
The trade-offs
What a framework gives you
- Fewer decisions: the common ones are already made, so you start building features sooner.
- A shared shape: projects on the same framework look alike, so what you learn from teammates, tutorials and answers online carries over.
- Solved problems: routing, sessions and sensible security defaults come built in. Django, for example, protects forms against cross-site request forgery out of the box.
What a framework costs
- A learning curve: you have to learn its conventions before you can be productive, and some behaviour feels like magic until you do.
- Lock-in: leaving a framework usually means rewriting the app, where leaving a library means changing a few calls.
- Upgrades: a major version can change the conventions your code depends on, and every part of the app has to follow.
- Poor fits: when your app needs something the framework was not designed for, you end up working around it.
What libraries give and cost
Libraries give you freedom: pick the best tool for each job, keep the app small and replace one piece without touching the rest. The price is that you write the glue between them, and every library is another dependency to keep updated and compatible with the others, which is how projects end up in dependency hell.
Common mistakes
- Using a framework for a small script. A 50-line tool that renames files does not need a project layout and a build step; plain code and a library or two will do.
- Installing a library for something the language already does. JavaScript's built-in
Intl.DateTimeFormatand Python'sdatetimehandle plenty of everyday date formatting, so check the standard library before adding a dependency. - Fighting the framework. Putting files where you prefer and working around its conventions gives you the costs of a framework without the benefits. Learn its way first, then decide if it fits.
- Treating a framework as the more serious choice. It trades freedom for structure, and that trade only pays off when your app needs the structure.
- Adding packages without checking them. Every library you install runs with your app's permissions, so check that it is maintained and widely used. Malicious packages do turn up in public registries.
Key takeaways
- A library is code you call. A framework is code that calls you.
- A framework decides your app's structure: where files live, how data flows and how features are built.
- Libraries trade structure for freedom, and frameworks trade freedom for structure.
- Most apps use both: a framework for the shape, libraries for particular jobs.
- Ask how much of your app a tool decides for you, not what it calls itself.