diff --git a/README.md b/README.md
index c0f4dc0..87811a6 100644
--- a/README.md
+++ b/README.md
@@ -122,6 +122,7 @@ console.log(buffer);
 | forceFloat32        | boolean        | false                         |
 | forceIntegerToFloat | boolean        | false                         |
 | ignoreUndefined     | boolean        | false                         |
+| undefinedAsNil      | boolean        | true                          |
 
 ### `decode(buffer: ArrayLike<number> | BufferSource, options?: DecoderOptions): unknown`
 
diff --git a/src/Encoder.ts b/src/Encoder.ts
index 43fcd8f..499d7c2 100644
--- a/src/Encoder.ts
+++ b/src/Encoder.ts
@@ -61,6 +61,15 @@ export type EncoderOptions<ContextType = undefined> = Partial<
      */
     ignoreUndefined: boolean;
 
+    /**
+     * If `true`, an object property with `undefined` value are encoded as `nil`, same as null.
+     * e.g. `{ foo: undefined }` will be encoded as `{ foo: nil }`.
+     * This flag has no effect if `ignoreUndefined` is `true`.
+     *
+     * Defaults to `true`.
+     */
+    undefinedAsNil: boolean;
+
     /**
      * If `true`, integer numbers are encoded as floating point numbers,
      * with the `forceFloat32` option taken into account.
@@ -82,6 +91,7 @@ export class Encoder<ContextType = undefined> {
   private readonly forceFloat32: boolean;
   private readonly ignoreUndefined: boolean;
   private readonly forceIntegerToFloat: boolean;
+  private readonly undefinedAsNil: boolean;
 
   private pos: number;
   private view: DataView;
@@ -99,6 +109,7 @@ export class Encoder<ContextType = undefined> {
     this.sortKeys = options?.sortKeys ?? false;
     this.forceFloat32 = options?.forceFloat32 ?? false;
     this.ignoreUndefined = options?.ignoreUndefined ?? false;
+    this.undefinedAsNil = options?.undefinedAsNil ?? true;
     this.forceIntegerToFloat = options?.forceIntegerToFloat ?? false;
 
     this.pos = 0;
@@ -174,7 +185,9 @@ export class Encoder<ContextType = undefined> {
       throw new Error(`Too deep objects in depth ${depth}`);
     }
 
-    if (object == null) {
+    if (object === null) {
+      this.encodeNil();
+    } else if (object === undefined && this.undefinedAsNil) {
       this.encodeNil();
     } else if (typeof object === "boolean") {
       this.encodeBoolean(object);
diff --git a/test/encode.test.ts b/test/encode.test.ts
index f1b6ffe..0efba93 100644
--- a/test/encode.test.ts
+++ b/test/encode.test.ts
@@ -1,5 +1,5 @@
 import assert from "assert";
-import { encode, decode } from "../src/index.ts";
+import { encode, decode, ExtensionCodec } from "../src/index.ts";
 
 describe("encode", () => {
   context("sortKeys", () => {
@@ -66,6 +66,40 @@ describe("encode", () => {
     });
   });
 
+  context("undefinedAsNil", () => {
+    const extensionCodec = new ExtensionCodec();
+    extensionCodec.register({
+      type: 0,
+      encode: (value) => (value === undefined ? new Uint8Array([0]) : null),
+      decode: () => undefined,
+    });
+
+    it("encodes { foo: undefined } as null by default", () => {
+      assert.deepStrictEqual(decode(encode({ foo: undefined, bar: 42 })), { foo: null, bar: 42 });
+    });
+
+    it("encodes { foo: undefined } as undefined with `undefinedAsNil: false`", () => {
+      assert.deepStrictEqual(
+        decode(encode({ foo: undefined, bar: 42 }, { extensionCodec, ignoreUndefined: false, undefinedAsNil: false }), {
+          extensionCodec,
+        }),
+        {
+          foo: undefined,
+          bar: 42,
+        },
+      );
+    });
+
+    it("encodes { foo: undefined } to { foo: null } with `undefinedAsNil: true`", () => {
+      assert.deepStrictEqual(
+        decode(encode({ foo: undefined, bar: 42 }, { extensionCodec, ignoreUndefined: false, undefinedAsNil: true }), {
+          extensionCodec,
+        }),
+        { foo: null, bar: 42 },
+      );
+    });
+  });
+
   context("ArrayBuffer as buffer", () => {
     const buffer = encode([1, 2, 3]);
     const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteLength);