Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactNext.jsJune 21, 20264 min read

Next.js Data Serialization: Managing State in Server Actions

Next.js Data Serialization is critical when passing complex types from Server Actions to client components. Learn how to handle non-serializable state safely.

Next.jsReactServer ActionsTypeScriptPerformanceWeb DevelopmentFrontend

Last month, our dashboard team hit a wall. We were trying to pass a complex class instance—complete with methods and localized date formats—directly from a Server Action into a client-side useState hook. The result? A silent, soul-crushing [object Object] error once the data crossed the network boundary.

If you're building production apps with Next.js, you've likely realized that the boundary between server and client isn't just a conceptual wall—it's a strict data contract. React expects JSON-serializable values. When you try to push anything else, the framework's internal serialization logic simply gives up.

The Reality of Next.js Data Serialization

When you trigger a Server Action, Next.js performs a POST request. The return value from that action is serialized into JSON, sent over the wire, and then rehydrated by the client. If your payload contains Date objects, Map instances, or custom classes, the default JSON.stringify approach destroys your data integrity.

I’ve seen engineers try to "fix" this by stringifying everything manually, but that just shifts the problem to the client, where you end up with redundant JSON.parse calls everywhere. Instead, we need to treat the serialized payload as a transport contract, not just a blob of data.

If you’re struggling with component boundaries, revisit React Server Components vs Client Components in Next.js to ensure you aren't leaking server-side logic into client contexts unnecessarily.

Implementing Custom Serializers

To manage complex state transfers, we need to define a consistent pattern for "serializing" and "deserializing" our domain models. I prefer creating a simple utility layer that explicitly transforms data before it leaves the server.

Here is a pattern we use to handle rich objects:

TYPESCRIPT
// utils/serializer.ts
export const serializeDomainModel = (data: ComplexModel) => ({
  ...data,
  createdAt: data.createdAt.toISOString(),
  metadata: JSON.stringify(data.metadata),
  type: CE9178">'MODEL_V1'
});

export const deserializeDomainModel = (data: any) => ({
  ...data,
  createdAt: new Date(data.createdAt),
  metadata: JSON.parse(data.metadata)
});

By wrapping the Server Action response in serializeDomainModel, you gain a single point of failure. If the schema changes, you update the serializer, not every single component that consumes the action.

Avoiding Hydration Mismatches

One of the most common issues during React hydration is a mismatch between the server-rendered state and the client's initial state. When we use Server Actions to update state, we are essentially bypassing the initial SSR render for that specific data.

If your serialized data is large, you might notice a performance dip around 150ms-200ms during the transition. To keep things snappy, avoid passing the entire database entity. Only pass the fields the UI actually needs. If you find your state management getting messy, remember to check your Next.js Request Memoization: Stop Over-Fetching in Server Components patterns to ensure you aren't fetching the same data twice.

When to Use Custom Serializers

Don't over-engineer this. If you're passing simple primitives, standard JSON is fine. You only need a custom serialization layer when:

  1. You are dealing with complex Date objects or specialized libraries (like Decimal.js).
  2. You need to keep the "shape" of your data consistent across different versions of your API.
  3. You are performing heavy validation on the client side before the data enters the component state.

I once spent about two days debugging a hydration error that turned out to be a BigInt value being passed through the action. The fix was trivial once we implemented a custom replacer function for JSON.stringify, but it taught me to be much more defensive about what I return from my actions.

FAQ: Common Serialization Pitfalls

Q: Can I pass functions through Server Actions? A: No. Functions are not serializable. If you need functionality on the client, define it in a shared utility file or a custom hook.

Q: Does Next.js support binary data in Server Actions? A: It does, but you'll need to handle it as FormData or Blob objects. If you're uploading files, stick to FormData.

Q: What happens if my serialization logic fails? A: Next.js will throw a runtime error during the serialization process. Always wrap your return values in a try-catch or use a schema validator like Zod to ensure your data matches the expected shape before it hits the network.

Final Thoughts

The key to managing Next.js data serialization is acknowledging that the network boundary is a hard stop. Don't fight the framework by trying to pass class instances or non-serializable objects. Instead, embrace the transition by transforming your data into a plain, transportable format at the edge of your Server Action.

Next time, I’d like to experiment with Zod-based automatic serialization to see if we can automate this process entirely, but for now, explicit converters have kept our production builds stable and our hydration errors to a minimum.

Back to Blog

Similar Posts

Next.jsReactJune 21, 20263 min read

Next.js App Router Data Revalidation: Mastering Cache Tags at Scale

Master Next.js App Router data revalidation using global cache tags. Learn to build automated, deterministic purge pipelines for complex data graphs.

Read more
Next.jsReact
June 21, 2026
4 min read

Next.js Server Actions Request Collapsing: Preventing Race Conditions

Master Next.js Server Actions request collapsing to prevent race conditions. Learn practical concurrency control patterns to stop redundant mutations today.

Read more
Next.jsReactJune 21, 20264 min read

Next.js Server Actions: Implementing Idempotency and Atomic Mutations

Master Next.js Server Actions by implementing idempotency keys and atomic mutations. Prevent duplicate requests and ensure data integrity in distributed systems.

Read more