Direct answer
Pick breakpoints from where your own content and components actually start to look wrong, not from a list of popular device widths, using two complementary methods together: build mobile-first and widen the browser until something breaks, then check your candidate breakpoints against your real traffic's viewport distribution so you don't over-invest in ranges nobody uses.
Method 1: mobile-first, watch where content breaks
Start authoring at the narrowest realistic width, roughly 320-375px, with fluid, percentage-based sizing and no breakpoints at all. Widen the viewport gradually; the first point where something visibly fails, a headline wraps awkwardly, a card grid has too much dead whitespace, a row could clearly fit one more item, is a candidate breakpoint specific to your actual UI, not a borrowed device number. Repeat this against your densest, most complex components (a data table, a multi-field form), since a narrow paragraph of body text will rarely dictate a real breakpoint on its own; dense components will.
Method 2: validate against real device data
Overlay your own analytics' viewport-width histogram against the candidate breakpoints from method 1. If a meaningful share of real traffic sits awkwardly between two content breakpoints with no different behavior defined for that range, that's a signal to add a breakpoint there; design effort should be biased toward where users actually are, not toward a theoretically clean set of numbers.
Formalizing the result
Round the candidate values to clean numbers and store them as named design tokens (for example bp-sm, bp-md, bp-lg) referenced by both design tools and code, so every component pulls from the same source instead of each one picking its own breakpoint independently.
When viewport breakpoints aren't the right tool
If the same component needs to behave differently depending on where it sits on the page, full-width in the main content area, narrow in a sidebar, a viewport breakpoint can't express that, since it only knows the screen size, not the component's actual available width. That's the case for container queries instead.
Worked example
A three-card feature-highlight section: at 320px, one column is the only sensible layout. Widening to roughly 560px, one column still reads fine. Around 600-620px, the single stacked column starts showing noticeably excess whitespace on either side, a candidate breakpoint near 600px to move to two columns. Two columns holds up until roughly 880-900px, where each card starts feeling cramped relative to the space available, a candidate breakpoint near 900px to move to three columns.
Now check against real traffic: suppose the site's viewport histogram clusters around 390px (a common phone width), 768px (tablet portrait), and 1440px (a common laptop width). Since 768px sits close to the 900px content breakpoint found above, and there's no meaningfully different layout need between them, round to a single breakpoint near 800px instead of keeping two breakpoints only 130px apart with identical behavior on either side.
Trade-offs and pitfalls
Chasing every device width in existence produces breakpoint sprawl that's expensive to test and maintain going forward. Ignoring real usage data in favor of "clean" content breakpoints alone can under-serve a real, sizable segment sitting awkwardly between two of them. Container queries solve the same-component-different-context problem that viewport breakpoints structurally cannot, but they add authoring complexity, so reach for them selectively, for shared components that genuinely appear in different-width contexts, rather than by default everywhere.