diff --git a/docs/dart/eliza-chat-screenshot.png b/docs/dart/eliza-chat-screenshot.png
new file mode 100644
index 00000000..3de14377
Binary files /dev/null and b/docs/dart/eliza-chat-screenshot.png differ
diff --git a/docs/dart/getting-started.md b/docs/dart/getting-started.md
index 71778f21..86201965 100644
--- a/docs/dart/getting-started.md
+++ b/docs/dart/getting-started.md
@@ -2,3 +2,423 @@
title: Getting started
sidebar_position: 1
---
+
+Connect-Dart is a small library (\<100KB!) that provides support for using
+generated, type-safe, and idiomatic Dart APIs to communicate with your app's
+servers using [Protocol Buffers (Protobuf)][protobuf]. It works with the Connect, gRPC, and gRPC-Web protocols.
+
+Imagine a world where you can jump right into building products
+and focus on the user experience without needing to handwrite REST/JSON
+endpoints or models — instead using generated APIs
+that utilize the latest Dart features and are guaranteed to match the server's modeling.
+
+In this guide, we'll use Connect-Dart to create a chat app for
+[ELIZA](https://en.wikipedia.org/wiki/ELIZA),
+a very simple natural language processor built in the 1960s to represent a
+psychotherapist. **The ELIZA service is
+[implemented using Connect-Go][go-demo], is
+[already up and running](https://connectrpc.com/demo) in production, and
+supports both the [gRPC-Web][grpc-web] and [Connect](../protocol.md)
+protocols - both of which can be used with Connect-Dart for this tutorial.**
+The APIs we'll be using are defined in a Protobuf schema that we'll use
+to generate a Connect-Dart client.
+
+This tutorial should take ~10 minutes from start to finish.
+
+## Prerequisites
+
+- [The Buf CLI][buf-cli] installed, and include it in the `$PATH`.
+- [Flutter][flutter] installed and setup for atleast one platform.
+
+## Create a new Flutter app
+
+Create a Flutter app called `eliza` by running:
+
+```bash
+flutter create eliza
+cd eliza
+```
+
+Next add a dependency on the `connectrpc` package by running the following:
+
+```bash
+flutter pub add connectrpc
+```
+
+## Define a service
+
+First, we need to add a Protobuf file that includes our service definition. For this tutorial, we are going to construct a unary endpoint for a service that is a stripped-down implementation of ELIZA, the famous natural language processing program.
+
+```bash
+$ mkdir -p proto && touch proto/eliza.proto
+```
+
+Open up the above file and add the following service definition:
+
+```proto
+syntax = "proto3";
+
+package connectrpc.eliza.v1;
+
+message SayRequest {
+ string sentence = 1;
+}
+
+message SayResponse {
+ string sentence = 1;
+}
+
+service ElizaService {
+ rpc Say(SayRequest) returns (SayResponse) {}
+}
+```
+
+Open the newly created `eliza.proto` file in the editor.
+This file declares a `connectrpc.eliza.v1` Protobuf package,
+a service called `ElizaService`, and a single method
+called `Say`. Under the hood, these components will be used to form the path
+of the API's HTTP URL.
+
+The file also contains two models, `SayRequest` and `SayResponse`, which
+are the input and output for the `Say` RPC method.
+
+## Generate code
+
+We're going to generate our code using [Buf][buf], a modern replacement for
+Google's protobuf compiler. We installed Buf earlier, but we also need a few
+configuration files to get going.
+
+First, scaffold a basic [`buf.yaml`][buf.yaml] by running `buf config init` at the root of your repository. Then, edit `buf.yaml`
+to use our `proto` directory:
+
+```yaml title=buf.yaml
+version: v2
+// highlight-next-line
+modules:
+// highlight-next-line
+ - path: proto
+lint:
+ use:
+ - DEFAULT
+breaking:
+ use:
+ - FILE
+```
+
+Next, tell Buf how to generate code by putting this into
+[`buf.gen.yaml`][buf.gen.yaml]:
+
+```yaml
+version: v2
+plugins:
+ - remote: buf.build/connectrpc/dart
+ out: lib/gen
+ - remote: buf.build/protocolbuffers/dart
+ out: lib/gen
+ include_wkt: true
+ include_imports: true
+```
+
+With those configuration files in place, we can now generate code:
+
+```bash
+$ buf generate
+```
+
+In your `lib/gen` directory, you should now see some generated Dart files:
+
+```
+lib/gen
+ ├── eliza.connect.client.dart
+ ├── eliza.connect.spec.dart
+ ├── eliza.pb.dart
+ ├── eliza.pbenum.dart
+ ├── eliza.pbjson.dart
+ └── eliza.pbserver.dart
+```
+
+The `.connect.client.dart` file contains a production client that conforms to `ElizaService`.
+
+The `.pb*.dart` files were generated by Google's
+[Dart plugin][dart-protobuf] and contains the corresponding Dart
+classes for the `SayRequest` and `SayResponse` messages we defined in our Protobuf file.
+
+At this point, your app should build successfully.
+
+## Integrate into the app
+
+Replace `main.dart` with:
+
+
+
+Click to expand main.dart
+
+
+```dart
+import 'package:eliza/gen/eliza.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:connectrpc/http2.dart';
+import 'package:connectrpc/connect.dart';
+import 'package:connectrpc/protobuf.dart';
+import 'package:connectrpc/protocol/connect.dart' as protocol;
+import './gen/eliza.connect.client.dart';
+
+final transport = protocol.Transport(
+ baseUrl: "https://demo.connectrpc.com",
+ codec: const ProtoCodec(), // Or JsonCodec()
+ httpClient: createHttpClient(),
+);
+
+void main() {
+ runApp(const ElizaApp());
+}
+
+class ElizaApp extends StatelessWidget {
+ const ElizaApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Eliza',
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
+ useMaterial3: true,
+ ),
+ home: ChatPage(transport: transport),
+ );
+ }
+}
+
+class ChatPage extends StatefulWidget {
+ const ChatPage({super.key, required this.transport});
+ final Transport transport;
+
+ @override
+ State createState() => _ChatPageState();
+}
+
+class _ChatPageState extends State {
+ final messages = List<({String sentence, bool byUser})>.empty(growable: true);
+ final currentSentence = TextEditingController();
+
+ void addMessage(String sentence, bool byUser) {
+ setState(() => messages.add((sentence: sentence, byUser: byUser)));
+ }
+
+ void send(String sentence) async {
+ addMessage(sentence, true);
+ final response = await ElizaServiceClient(widget.transport).say(
+ SayRequest(sentence: sentence),
+ );
+ addMessage(response.sentence, false);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Expanded(
+ child: ListView(
+ children: [
+ for (final message in messages)
+ Column(
+ key: ObjectKey(message),
+ children: [
+ if (message.byUser) ...[
+ const Row(
+ children: [
+ Spacer(),
+ Text(
+ "You",
+ style: TextStyle(
+ color: Colors.grey,
+ fontWeight: FontWeight.w600,
+ ),
+ )
+ ],
+ ),
+ Row(
+ children: [
+ const Spacer(),
+ Text(
+ message.sentence,
+ textAlign: TextAlign.left,
+ ),
+ ],
+ )
+ ] else ...[
+ const Row(
+ children: [
+ Text(
+ "Eliza",
+ style: TextStyle(
+ color: Colors.blue,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ Spacer(),
+ ],
+ ),
+ Row(
+ children: [
+ Text(
+ message.sentence,
+ textAlign: TextAlign.left,
+ ),
+ const Spacer(),
+ ],
+ )
+ ]
+ ],
+ )
+ ],
+ ),
+ ),
+ Row(
+ children: [
+ Flexible(
+ child: TextField(
+ controller: currentSentence,
+ decoration: const InputDecoration(
+ hintText: 'Write your message...',
+ border: UnderlineInputBorder(),
+ ),
+ ),
+ ),
+ TextButton(
+ onPressed: () {
+ final sentence = currentSentence.text;
+ if (sentence.isEmpty) {
+ return;
+ }
+ send(sentence);
+ currentSentence.clear();
+ },
+ child: const Text(
+ 'Send',
+ style: TextStyle(color: Colors.blue),
+ ),
+ )
+ ],
+ )
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
+```
+
+
+
+Build and run the app, and you should be able to chat with Eliza! 🎉
+
+import ElizaChatScreenshot from './eliza-chat-screenshot.png';
+
+
+
+## Breaking it down
+
+Let's dive into what some of the code above is doing, particularly regarding
+how it is interacting with the Connect library.
+
+### Creating a `Transport`
+
+At the very top, it creates an instance of `Transport`. This is responsible for configuring various options like serialization (i.e., JSON or Protobuf), http client (in this case an [http2][http2] client) and the protocol itself (in this case the [Connect protocol](../protocol.md)).
+
+If we wanted to use JSON instead of Protobuf we'd only need to make a simple line change:
+
+```dart
+final transport = Transport(
+ baseUrl: "https://demo.connectrpc.com",
+ //highlight-next-line
+ codec: const JsonCodec(),
+ httpClient: createHttpClient(),
+);
+```
+
+The Http client can be changed to use `dart:io` (it only supports H/1), a `fetch` based implementation for flutter web, or by creating a new implementation for the `HttpClient` type. For more customization options,
+see the [documentation on using clients](./using-clients.md)
+
+### Using gRPC
+
+If you'd like to use gRPC as the transport protocol in the above example,
+simply change the following 2 lines:
+
+```dart
+import 'package:eliza/gen/eliza.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:connectrpc/http2.dart';
+import 'package:connectrpc/protobuf.dart';
+//highlight-next-line
+import 'package:connectrpc/protocol/grpc.dart' as protocol;
+import './gen/eliza.connect.client.dart';
+
+final transport = protocol.Transport(
+ baseUrl: "https://demo.connectrpc.com",
+ codec: const JsonCodec(), // Or JsonCodec()
+ httpClient: createHttpClient(),
+ //highlight-next-line
+ statusParser: const StatusParser()
+);
+```
+
+### Using the generated code
+
+Take a look at the `ChatPage` widget above. It is initialized with a `Transport` interface. Accepting an interface allows for injecting mocks into widgets for testing. We won't get into mocks and testing here, but
+you can check out the [testing docs](testing.md) for details and examples.
+
+Whenever the `send(...)` method is invoked, we create an `ElizaServiceClient` from a transport and pass the request to the `say(...)` method on the generated client and await a response from the server.
+All of this is done using type-safe generated APIs from the Protobuf
+file we wrote earlier.
+
+Creating clients is free, as they are extension types on the `Transport`. This takes away the need to create and pass different clients to different pages/widgets. Instead, all of widgets only need the `Transport` type and can maintain a local value of the extension type without any cost.
+
+## Using gRPC or gRPC-Web
+
+Connect-Dart supports the [Connect](../protocol.md),
+[gRPC][grpc], and [gRPC-Web][grpc-web] protocols.
+Instructions for switching between them
+can be found [here](./using-clients.md).
+
+We recommend using Connect-Dart over [gRPC-Dart][grpc-dart] even if you're
+using the gRPC protocol for a few reasons:
+
+- **Idiomatic, typed APIs.** No more hand-writing REST/JSON endpoints and
+ models. Connect-Dart
+ [generates](./using-clients.md#using-generated-clients) idiomatic
+ APIs that utilize the latest Dart features such as extensions and
+ eliminates the need to worry about serialization.
+- **First-class testing support.** Connect-Dart comes with a mockable `Transport` that enables easy [testability](./testing.md) with minimal handwritten boilerplate.
+- **Easy-to-use tooling.** Connect-Dart integrates with the Buf CLI,
+ enabling remote code generation without having to install and configure
+ local dependencies.
+- **Flexibility.** Connect-Dart supports swapping http clients with built in
+ support for `dart:io`, `fetch` and `http2`. It also supports unified [interceptors](./interceptors.md).
+- **Binary size.** The Connect-Dart library is very small (\<100KB)
+ and tree shakable to only include the protocol code in use.
+
+If your backend services are already using gRPC today,
+[Envoy provides support][envoy-grpc-bridge]
+for converting requests made using the Connect and gRPC-Web protocols to gRPC,
+enabling you to use Connect-Dart without the [http2][http2] dependency.
+
+[buf]: https://buf.build/docs/
+[buf-cli]: https://buf.build/docs/installation
+[buf.gen.yaml]: https://buf.build/docs/configuration/v2/buf-gen-yaml
+[buf.yaml]: https://buf.build/docs/configuration/v2/buf-yaml
+[dart-protobuf]: https://pub.dev/packages/protobuf
+[envoy-grpc-bridge]: https://www.envoyproxy.io/docs/envoy/latest/
+[flutter]: https://docs.flutter.dev/get-started/install
+[go-demo]: https://github.com/connectrpc/examples-go
+[grpc]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
+[grpc-dart]: https://pub.dev/packages/grpc
+[grpc-web]: https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-WEB.md
+[http2]: https://pub.dev/packages/http2
+[protobuf]: https://developers.google.com/protocol-buffers
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 7d3b739f..9dd7b918 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -173,7 +173,7 @@ const config = {
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
- additionalLanguages: ["kotlin", "protobuf", "swift"],
+ additionalLanguages: ["kotlin", "protobuf", "swift", "dart"],
},
}),
};
diff --git a/src/components/home/Guides.tsx b/src/components/home/Guides.tsx
index 013be527..55607995 100644
--- a/src/components/home/Guides.tsx
+++ b/src/components/home/Guides.tsx
@@ -100,6 +100,13 @@ export default function Guides() {
title="Android guide"
description="Kotlin clients available"
/>
+
diff --git a/static/img/logos/dart.svg b/static/img/logos/dart.svg
new file mode 100644
index 00000000..a5ea786e
--- /dev/null
+++ b/static/img/logos/dart.svg
@@ -0,0 +1,19 @@
+
+
+