Skip to main content

News Lister

Vue 3 news aggregator with keyword search, pagination, and responsive design

Role: Interview Take-Home ProjectPeriod: 20234 min read
Vue 3TypeScriptpnpmREST APIVite
Vue 3
Composition API
Keyword
search with suggestions
Pagination
10 articles per page

Interview coding exercise — built for demonstration


A news article explorer built as a Vue.js coding exercise. It consumes a REST API to fetch news stories, renders article thumbnails with headlines, and provides links to full stories — all with a clean, responsive interface.

News Lister app mockup

The Brief

The exercise required building a Vue.js application that consumes a REST API returning JSON objects — each containing thumbnail images, headlines, and source URLs for news articles about a given topic. The application needed to render article cards with images, provide clickable links to the full stories, support keyword search with autocomplete suggestions, and handle empty states gracefully when no articles match the current search term.

What made this interesting was the combination of requirements: the search needed to feel responsive without hammering the API on every keystroke, the pagination needed to work with the search state (not independently), and the empty state needed to distinguish between "no results for this query" and "API returned an error."

Vue 3
Composition API
TypeScript
Type safety
pnpm
Package manager
Vite
Build tool

Search and Pagination

The search feature combines a text input with an autocomplete dropdown. As the user types, a debounced API call fetches matching article titles and displays them as suggestions below the input. Selecting a suggestion or pressing Enter triggers the full search, which fetches a paginated list of results — 10 articles per page — and renders them as a responsive card grid.

The pagination controls sit below the grid and update the query parameter without triggering a full page reload. The current page is tracked in the component state alongside the search query, so changing pages preserves the search term. This sounds obvious in retrospect, but the first implementation had page resetting to 1 on every search — a subtle UX bug that took a while to notice.

Architecture and Extensibility

The application separates data fetching from rendering through a composable-based architecture. A useNewsFeed composable handles API calls, pagination state, and search debouncing. The view components focus entirely on rendering — displaying article cards, managing the autocomplete dropdown, and showing the pagination bar.

This separation means the application can switch between news API providers by changing the composable's fetch logic, without touching any component. The composable returns a consistent interface (articles, loading, error, search, paginate), and the components don't care how those values are populated.

Tip·

Architecture insight

The application is designed with a clean separation between data fetching, state management, and rendering. Swapping out the news feed source or modifying the UI components doesn't require changes to the core rendering logic — making it extensible by design.

Handling Edge Cases

The empty state handling went through several iterations. The first version simply showed "No articles found" when the results array was empty. But this message was misleading when the API returned an error — the user saw "No articles found" when the real problem was a network failure. The fix was to track error state separately from the results array, so the UI can distinguish between "no results" (valid search, zero matches) and "something broke" (network error, API limit, invalid key).

What I Learned

Designing for extensibility taught me that anticipating future requirements doesn't mean over-engineering. A simple interface contract between the data layer and the rendering layer — the composable returns articles, loading, and error — is enough to support future changes without locking in architectural decisions too early.

TypeScript with Vue 3's Composition API provides an excellent developer experience. Autocompletion in the template, refactoring support across composables, and compile-time error catching all contributed to a faster development cycle than plain JavaScript would have allowed. The type system caught several issues where I was accessing properties on potentially null objects — exactly the kind of bug that's hard to find at runtime.

Responsive design for card-based layouts requires thinking about content reflow, not just column count. On desktop, each card shows the full headline and a generous thumbnail. On mobile, the same card shows a truncated headline and a smaller image. The card component needs to know about its own responsive behavior, which means media queries in the component scope rather than a global layout breakpoint.

Warning·

Nuxt?

This was built as a plain Vue 3 SPA with Vite. The exercise specifically required a single-page application setup rather than a framework with built-in routing. This kept the scope focused on component composition and API integration patterns.

Scripts

  • pnpm dev — Development server
  • pnpm build — Production build
  • pnpm test — Run tests with coverage
  • pnpm lint — Lint source code
  • pnpm format — Format with Prettier

Built as a coding exercise. View source · Live demo