Our Summer Sale is Live! 🎉
Everything 30% off with code SUMMER30! (Excl. Team and VS Pro)
00
Days
00
Hrs
00
Min
00
Sec
Get 30% off anything!

Sending email with C# in .NET

06/09/2025

Email is everywhere! Notifications, receipts, authentication flows.

This guide is a hands-on walkthrough to help you get your .NET application sending emails, ditching the obsolete System.Net.Mail.SmtpClient class, and using the modern, recommended MailKit (to send), MimeKit (to build messages), and MailPit (to test/receive). Along the way, we’ll provide a bit of context on SMTP (the protocol) and MIME (the standard that extends the format of email messages, allowing for attachments and different character set support).


SmtpClient vs MailKit (a quick mental model)

  • SmtpClient: built-in, simple, missing modern auth, marked obsolete.
  • MailKit: send emails via SMTP.
  • MimeKit: build emails (plain, HTML, attachments, inline images).
  • MailPit: local SMTP server + web UI for testing.

Installing MailPit

For development, we’ll run MailPit locally to capture and view emails. It provides:

  • An SMTP server on port 1025 (where your app connects).
  • A web UI on port 8025 (to view messages).

There are a number of ways to install and run MailPit locally but here we'll use the Docker approach. If you don't have Docker, you can download and run the binary directly from the MailPit website.

docker run -d --name mailpit -p 1025:1025 -p 8025:8025  axllent/mailpit

Then open your browser at http://localhost:8025 — MailPit should be running and waiting for messages.

Screenshot of MailPit

What we just did: started a local SMTP relay (port 1025) and a web UI (port 8025) to catch and inspect emails safely during development.

Reference: MailPit Install Docs


Console app setup

dotnet new console -o MailDemo
cd MailDemo
dotnet add package MailKit

Clear out your Program.cs file, and replace it with the following:

using MimeKit;
using MailKit.Net.Smtp; // ⚠️ make sure this is MailKit, not System.Net.Mail

var msg = new MimeMessage();
msg.From.Add(new MailboxAddress("App", "[email protected]"));
msg.To.Add(new MailboxAddress("User", "[email protected]"));
msg.Subject = "Hello from .NET!";
msg.Body = new TextPart("plain") { Text = "This is a test email." };

using var smtp = new SmtpClient();
await smtp.ConnectAsync("localhost", 1025, false);
await smtp.SendAsync(msg);
await smtp.DisconnectAsync(true);

Note: We are using the SmtpClient class from MailKit.Net.Smtp, not the obsolete System.Net.Mail namespace.

Now run dotnet run, navigate to your MailPit inbox (http://localhost:8025), and you should see the email your app just created and sent!

Screenshot of MailPit Screenshot of MailPit

What we just did: Created a new .NET console app, and added some code which sends a plaintext email to our local MailPit SMTP server.


Beyond plain text - SMTP and MIME

Let's take another look at the message we have just sent using the 'Headers' and 'Raw' tabs in MailPit.

Screenshot of MailPit Screenshot of MailPit

Our first email sent a basic plain-text message. This works because SMTP, the protocol that underpins email, was originally designed in the early 1980s to transmit nothing more than 7-bit ASCII text. That’s fine for simple messages, but it has drawbacks:

  • No support for non-English characters (accents, non-Latin scripts, etc.).
  • No way to format messages with HTML.
  • No attachments (PDFs, images, spreadsheets).
  • No inline images or rich media.

This is where MIME (Multipurpose Internet Mail Extensions) comes in. MIME is a standard that extends the format of email messages. It allows:

  • Encoding of arbitrary data (e.g. binary files → base64).
  • Multiple parts in a single email (plain + HTML versions).
  • Inline resources like images.
  • Specifying character sets (e.g. UTF-8).

MIME is basically a way to encode any kind of data as 7-bit ASCII, and have the email client decode at the other end.

With MailKit and MimeKit, you don’t need to worry about manually writing MIME headers — the libraries generate the correct structure for you. All you do is declare what you want: HTML body, an attachment, or an inline resource.

Let’s spice up our messages step by step.


Adding an HTML body with MimeKit

Replace the line

message.Body = new TextPart("plain") { Text = "This is a test email." };

with

var bb = new BodyBuilder();
bb.TextBody = "This is a test email.";
bb.HtmlBody = "<h1>Hello World</h1><p>This is <b>HTML</b> email.</p>";
message.Body = bb.ToMessageBody();

Run the program again and view the new email in MailPit.

Screenshot of MailPit

We can see that our email is now coming through in HTML as expected. By clicking the Text tab, we can see that we are still sending the original text version as well.

Digging a bit under the hood, we can select the Raw tab to see how the MimeKit library and BodyBuilder class have constructed this multipart email using the MIME standard.

Screenshot of MailPit

The main Content-Type is set to multipart/alternative with a generated boundary string (a unique string of ASCII characters). This is then used to split the subsections for our text/plain and text/html bodies.

What we just did: used MimeKit’s BodyBuilder to replace our plain-text message body with a multipart body that includes both plain text and HTML.


Adding an attachment to the email

Sometimes you want to send more than just text — invoices, PDFs, images, or reports.
With MimeKit, attachments are handled through the BodyBuilder.Attachments collection.

Update your code and add an attachment like this:

bb.Attachments.Add("Dometrain.png");

Run the program again and check MailPit. You should see the attachment listed in the UI.

Screenshot of MailPit

Behind the scenes, MimeKit has taken care of:

  • Encoding the binary file into Base64 (so it can travel over SMTP’s 7-bit ASCII pipe).
  • Setting the correct Content-Type header based on the file type.
  • Adding a Content-Disposition: attachment header with the filename.

Screenshot of MailPit

There is also the inline alternative: add the image as a LinkedResource and embed it directly in the HTML.

var img = bb.LinkedResources.Add("Dometrain.png");
img.ContentId = MimeUtils.GenerateMessageId();
bb.HtmlBody = $"<img src=\"cid:{img.ContentId}\">";

Screenshot of MailPit

Note: as seen above, MailPit will also unpack the inline image / linked resource as an attachment. Each email client can handle this differently and you have limited control as the sender.

What we just did: added a file attachment to our email. MimeKit handled the MIME encoding and headers automatically — we just told it which file to attach.


Wrapping up

We started with the basics — getting a console app to send a plain-text email through MailPit — and then layered on richer capabilities:

  • Plain text → HTML → multipart: how MIME extends email beyond 7-bit ASCII.
  • Attachments and inline images: how MimeKit makes encoding and structure simple.
  • Local testing with MailPit: safe, fast feedback without hitting real inboxes.

If you want to go further into production-ready email systems, check out our courses where we cover all of the above plus:

  • Templates: don’t hand-roll HTML — use MJML or Razor for maintainable designs.
  • Background jobs: offload email sends from the request thread to keep your app responsive.
  • Tests: assert against MimeMessage objects to guarantee the right mail goes out.
  • Providers: choosing between SMTP relays and APIs (analytics, deliverability, cost).

A tiny checklist

  • [ ] Use MailKit + MimeKit, not System.Net.Mail.SmtpClient.
  • [ ] Run MailPit locally for safe testing.
  • [ ] Always include plain text + HTML parts for compatibility.
  • [ ] Use attachments for files, inline resources for embedded images.

About the Author

author_img

Nick Chapsas

Nick Chapsas is a .NET & C# content creator, educator and a Microsoft MVP for Developer Technologies with years of experience in Software Engineering and Engineering Management.

He has worked for some of the biggest companies in the world, building systems that served millions of users and tens of thousands of requests per second.

Nick creates free content on YouTube and is the host of the Keep Coding Podcast.

More courses by Nick Chapsas