Skip to content

A field's type can be another message, whether it is defined in the same file, elsewhere in the package, or in an import, and a message can be declared inside another when the inner type only makes sense there.

It also gives teams one definition of a shared shape. If User, Event, and Office all use the same Location message, the code generated for every language agrees on what a location contains.

On the wire an embedded message is length-delimited, which means its bytes are preceded by their length. A decoder that lacks the inner schema can still skip the whole sub-message and read the fields after it.

message Result {
  string url = 1;
  string title = 2;
}

message SearchResponse {
  Result top_result = 1;
}
JSON
{
  "topResult": {
    "url": "https://protobuf.com/",
    "title": "Protobuf Explained"
  }
}

An embedded message travels as a block: a byte count up front, then the inner message's own bytes.

On the Bytes tab, click the outer segment: the byte count in front is what lets a decoder skip a sub-message it doesn't understand.

schema.proto
edition = "2024";

package demo.v1;

message SearchResponse {
  Result top_result = 1;
  repeated Result results = 2;
}

message Result {
  string url = 1;
  string title = 2;
}
Input JSON (editable)
{
  "topResult": {
    "url": "https://protobuf.com/",
    "title": "Protobuf Explained"
  }
}
ProtoJSON

Compiling the schema...

Next

Repeated Fields

Representing lists in Protobuf with repeated fields, and how packed encoding stores repeated scalars in a single length-prefixed block.