Most developers think SEO is someone else's problem. Add some meta tags, write a few blog posts, and let the marketing team handle the rest. That works fine for a brochure site. For web applications, especially single page applications built with React, Next.js, Vue, or Angular, technical SEO requires deliberate engineering decisions that affect your architecture from the ground up.
We have seen applications with outstanding content and strong domain authority rank poorly because the technical foundation was wrong. JavaScript rendered content that Google could not index. Crawl budgets wasted on infinite URL variations. Core Web Vitals so poor that Google actively demoted the pages. These are engineering problems, not marketing problems.
This post covers the technical SEO fundamentals that every development team building a web application needs to understand.
Rendering Strategy Is Your Most Important SEO Decision
How your application renders HTML determines whether search engines can even see your content. There are three primary approaches, and the choice has significant SEO implications.
Server Side Rendering (SSR) generates full HTML on the server for every request. Search engine crawlers receive complete page content immediately, making indexing reliable and fast. Frameworks like Next.js, Nuxt.js, and SvelteKit support SSR out of the box. The trade off is higher server costs and more complex deployment.
Static Site Generation (SSG) pre renders pages at build time. This produces the fastest possible load times and is ideal for content that does not change frequently, like blog posts, documentation, and landing pages. The downside is that builds can become slow for sites with thousands of pages, and content is only as fresh as the last build.
Client Side Rendering (CSR) renders everything in the browser via JavaScript. This is the default for most React SPAs. While Google claims it can render JavaScript, in practice, CSR causes indexing issues for 20 to 30% of pages based on what we have observed. Google has a rendering queue, and your JavaScript rendered content may wait days or weeks to be indexed. For any page you want ranked, CSR is a risk.
Our recommendation: use SSR or SSG for any page that needs to be indexed. Reserve CSR for authenticated dashboards, admin interfaces, and other content that search engines do not need to see. We covered performance implications of these choices in our website performance optimization guide.
Core Web Vitals Are a Ranking Factor
Google uses three Core Web Vitals metrics as direct ranking signals.
Largest Contentful Paint (LCP) measures how long it takes for the largest visible content element to render. Target: under 2.5 seconds. Common causes of poor LCP: unoptimized images, render blocking CSS and JavaScript, slow server response times, and lazy loading above the fold content (do not lazy load the hero image).
Cumulative Layout Shift (CLS) measures visual stability. Target: under 0.1. Every time an element shifts position after it becomes visible, it hurts CLS. The most common causes: images without explicit width and height attributes, ads or embeds that load late and push content down, fonts that swap and change text size, and dynamic content injected above existing content.
Interaction to Next Paint (INP) measures responsiveness. Target: under 200 milliseconds. When a user clicks a button, how long until the browser responds visually? Heavy JavaScript execution, long running event handlers, and main thread blocking are the primary causes. Breaking up long tasks with `requestIdleCallback` or using Web Workers for heavy computation helps.
Improving Core Web Vitals requires engineering work, not just configuration. Image optimization, code splitting, font loading strategies, critical CSS extraction, and server response time optimization are all development tasks. On projects we have shipped, addressing Core Web Vitals typically involves touching the build pipeline, CDN configuration, component architecture, and image handling.
Crawlability and Indexation
If Google cannot find your pages, nothing else matters. Here is how to make your application crawlable.
Generate and maintain an XML sitemap. Your sitemap should list every page you want indexed, with accurate `lastmod` dates. For dynamic applications, generate the sitemap programmatically from your database. Exclude URLs you do not want indexed (admin pages, duplicate parameter variations, user profile pages). Submit the sitemap in Google Search Console.
Implement a clean URL structure. URLs should be descriptive, lowercase, hyphenated, and static. `/products/blue-widget` is good. `/products?id=47&color=blue&ref=nav` is bad. Query parameters create crawl budget waste when they generate the same content with different URLs. Use canonical tags to handle parameter variations.
Use canonical tags correctly. Every page should have a `` tag pointing to the definitive URL for that content. If the same product appears at `/products/widget` and `/category/widgets/widget`, both pages should have a canonical tag pointing to whichever URL you consider primary.
Manage your robots.txt. Block paths you do not want crawled: API endpoints, admin panels, search result pages with faceted filters, and staging environments. Do not block CSS or JavaScript files, as Google needs these to render your pages.
Handle pagination properly. If you have paginated content (product listings, blog archives), each page should be indexable with a unique canonical URL. Infinite scroll with no paginated fallback means Google only sees the first page of results. Use `rel="next"` and `rel="prev"` where applicable, or better yet, provide static paginated URLs that load full content server side.
Structured Data and Rich Results
Structured data (JSON LD) helps search engines understand your content and can generate rich results like star ratings, FAQ accordions, breadcrumbs, and product pricing directly in search results.
Implement structured data for your content types. Common schemas include: `Organization` (your company info), `BreadcrumbList` (navigation path), `Article` or `BlogPosting` (blog content), `Product` (ecommerce), `FAQPage` (frequently asked questions), and `HowTo` (tutorial content).
Place JSON LD in a `