When to use nvarchar(max) in SQL Server

When to use nvarchar(max) in SQL Server

🧠 When to Use nvarchar(max) in SQL Server

...and when not to — because I’ve already done the hard thinking for you.


Choosing the right field size for large strings in SQL Server is deceptively tricky. Go too small, and you risk truncation. Go nvarchar(max) too early, and you might trigger performance issues due to LOB (Large Object) storage.

I’ve had to solve this in real-world apps handling everything from SEO content to user-submitted articles. If you’re working on a content-heavy project — blog platforms, CMS, product catalogs — you’re in the right place. Here's the distilled wisdom:


💡 The Problem with nvarchar(max)

SQL Server treats nvarchar(max) fields differently. Once your row exceeds 8,060 bytes, it spills content into separate LOB pages — which:

  • Adds I/O overhead
  • Slows queries
  • Messes with indexing

That’s fine for long-form content like blog posts or logs. But if most of your rows don’t need that much space, there’s a better way.


🧮 What Actually Fits in a Row?

  • nvarchar stores 2 bytes per character
  • The row size limit is ~8 KB
  • So… a single nvarchar(4000) column = 8,000 bytes

✅ Just under the wire

⚠️ But that leaves almost no room for other fields


🛠 My Real-World Tradeoff Sizes

TypeCharsSizeWhen to Use It
nvarchar(4000)4,000~8 KBSafest upper limit for in-row storage
nvarchar(4500)4,500~9 KBRisk of spillover, but tolerable in slim tables
nvarchar(6000)6,000~12 KBFrequent LOB use; acceptable for long-form content
nvarchar(max)2 GBLOB onlyNeeded only when unpredictable sizes are common

🧪 What I Use in My DevStack

In my own codebase, I balance performance with flexibility like this:

✅ Default:

[StringLength(4000)]
public string? Description { get; set; }

  • Fully indexable
  • Stored in-row
  • Fast for search and display

🚀 Large Content Mode:

[Column(TypeName = "nvarchar(max)")]
public string? Body { get; set; }

  • Used for blog posts, email bodies, and content blocks
  • Stored off-row only when needed

💡 Hybrid Pattern:

Table: Articles
- Title (nvarchar(200))
- Summary (nvarchar(4000)) -- In-row summary
- Body (nvarchar(max))     -- Full article

I can render fast previews using just Summary, and lazy-load the Body only when needed.


🔥 Pro Tip: Avoid Surprises in EF Core

If you're using Entity Framework Core:

  • Don't use [StringLength(8000)]SQL Server will reject it.
  • Either:
  • Stick to 4000 (max in-row for Unicode), or
  • Explicitly set to nvarchar(max) with [Column(TypeName = "nvarchar(max)")]

📦 Indexing Considerations

  • You can’t create a regular index on fields longer than 900 bytes
  • Use Full-Text Search for nvarchar(max)
  • For frequently searched values, split into a separate column or table

✨ Summary: What Should You Use?

Use CaseRecommendation
Article summariesnvarchar(4000)
Long content (blogs, logs)nvarchar(max)
Hybrid: summary + bodynvarchar(4000) + max
Indexable + fast display textnvarchar(4000) or smaller

🚀 Already Solved in My Stack

All this logic is already baked into my ASP.NET Core DevStack, where I:

  • Use nvarchar(4000) by default
  • Dynamically switch to nvarchar(max) where needed
  • Avoid LOB performance hits unless absolutely necessary
  • Preconfigure indexing and full-text support

You don’t need to guess what size is “safe.” I’ve built it, tested it, and deployed it — so you can drop it in and move on.

🔗 See how I architect it →