SlackNetBlockBuilder 0.6.2-beta
dotnet add package SlackNetBlockBuilder --version 0.6.2-beta
NuGet\Install-Package SlackNetBlockBuilder -Version 0.6.2-beta
<PackageReference Include="SlackNetBlockBuilder" Version="0.6.2-beta" />
<PackageVersion Include="SlackNetBlockBuilder" Version="0.6.2-beta" />
<PackageReference Include="SlackNetBlockBuilder" />
paket add SlackNetBlockBuilder --version 0.6.2-beta
#r "nuget: SlackNetBlockBuilder, 0.6.2-beta"
#:package SlackNetBlockBuilder@0.6.2-beta
#addin nuget:?package=SlackNetBlockBuilder&version=0.6.2-beta&prerelease
#tool nuget:?package=SlackNetBlockBuilder&version=0.6.2-beta&prerelease
SlackNetBlockBuilder
A fluent builder extension for SlackNet that simplifies creating Slack Block Kit UI elements with a clean, chainable API.
This project is not affiliated with Slack or SlackNet. I just needed a better way to create blocks for my Slack apps.
This package is currently in beta. The code passes all tests, but breaking changes may occur until the v1 release.
Installation
Install the package via NuGet:
dotnet add package SlackNetBlockBuilder
Visual Studio
Install-Package SlackNetBlockBuilder
Getting Started
Here are some examples to help you get started with SlackNetBlockBuilder:
Simple Message
using SlackNet;
using SlackNet.Blocks;
using SlackNet.WebApi;
// Create a new block builder
var blocks = BlockBuilder.Create()
.AddSection("Hello, world!")
.AddDivider()
.AddSection(section => section
.Text("This is a section with some formatted text")
.Accessory(button => button
.Text("Click Me")
.ActionId("button_click")
.Primary()))
.Build();
// Use with SlackNet to send a message
await slackApi.Chat.PostMessage(new Message
{
Channel = channelId,
Blocks = blocks
});
Interactive Actions
var blocks = BlockBuilder.Create()
.AddSection("Please select an option:")
.AddActions(actions => actions
.Button(button => button
.Text("Approve")
.ActionId("approve_button")
.Primary())
.Button(button => button
.Text("Reject")
.ActionId("reject_button")
.Danger())
.StaticSelect(select => select
.Placeholder("Choose an option")
.ActionId("select_option")
.AddOption("Option 1", "value1")
.AddOption("Option 2", "value2")
.AddOption("Option 3", "value3")))
.Build();
Forms with Input Elements
var blocks = BlockBuilder.Create()
.AddHeader("Submit Request Form")
.AddInput<PlainTextInput>("Title", input => input
.ActionId("title_input")
.Placeholder("Enter a title")
.Required())
.AddInput<PlainTextInput>("Description", input => input
.ActionId("description_input")
.Multiline()
.Placeholder("Enter a detailed description")
.Optional())
.AddInput<DatePicker>("Due Date", input => input
.ActionId("due_date_input")
.Placeholder("Select a date")
.Optional())
.AddActions(actions => actions
.Button(button => button
.Text("Submit")
.ActionId("submit_form")
.Primary())
.Button(button => button
.Text("Cancel")
.ActionId("cancel_form")))
.Build();
Updating Messages
// When handling an interaction payload
public async Task HandleInteraction(InteractionPayload payload)
{
// Create a new block builder from the existing blocks
var updatedBlocks = BlockBuilder.From(payload.Message.Blocks)
// Remove a specific block by ID
.Remove("status_block")
// Add a new status section
.AddSection(section => section
.BlockId("status_block")
.Text("✅ Request approved!"))
.Build();
// Update the message
await slackApi.Chat.Update(new MessageUpdate
{
Channel = payload.Channel.Id,
Ts = payload.Message.Ts,
Blocks = updatedBlocks
});
}
Comparison with Traditional Approach
The following examples show how SlackNetBlockBuilder compares to the traditional approach of creating blocks manually.
Simple Message Creation
With SlackNetBlockBuilder:
using SlackNet;
using SlackNet.Blocks;
using SlackNet.WebApi;
// Create a new block builder
var blocks = BlockBuilder.Create()
.AddSection("Hello, world!")
.AddDivider()
.AddSection(section => section
.Text("This is a section with some formatted text")
.Accessory(button => button
.Text("Click Me")
.ActionId("button_click")
.Primary()))
.Build();
// Use with SlackNet to send a message
await slackApi.Chat.PostMessage(new Message
{
Channel = channelId,
Blocks = blocks
});
Without SlackNetBlockBuilder (Traditional Approach):
With simple messages, the difference in terms of lines of code is negligible, but you will find that creating a message is easier and faster to type. Here is the same example as above without the BlockBuilder:
using SlackNet;
using SlackNet.WebApi;
using SlackNet.Blocks;
// Create blocks manually
var blocks = new List<Block>
{
new SectionBlock
{
Text = new PlainText { Text = "Hello, world!" }
},
new DividerBlock(),
new SectionBlock
{
Text = new PlainText { Text = "This is a section with some formatted text" },
Accessory = new Button
{
Text = new PlainText { Text = "Click Me" },
ActionId = "button_click",
Style = ButtonStyle.Primary
}
}
};
// Use with SlackNet to send a message
await slackApi.Chat.PostMessage(new Message
{
Channel = channelId,
Blocks = blocks
});
Interactive Actions
With SlackNetBlockBuilder:
var blocks = BlockBuilder.Create()
.AddSection("Please select an option:")
.AddActions(actions => actions
.Button(button => button
.Text("Approve")
.ActionId("approve_button")
.Primary())
.Button(button => button
.Text("Reject")
.ActionId("reject_button")
.Danger())
.StaticSelect(select => select
.Placeholder("Choose an option")
.ActionId("select_option")
.AddOption("Option 1", "value1")
.AddOption("Option 2", "value2")
.AddOption("Option 3", "value3")))
.Build();
Without SlackNetBlockBuilder (Traditional Approach):
var blocks = new List<Block>
{
new SectionBlock
{
Text = new PlainText { Text = "Please select an option:" }
},
new ActionsBlock
{
Elements = new List<IActionElement>
{
new Button
{
Text = new PlainText { Text = "Approve" },
ActionId = "approve_button",
Style = ButtonStyle.Primary
},
new Button
{
Text = new PlainText { Text = "Reject" },
ActionId = "reject_button",
Style = ButtonStyle.Danger
},
new StaticSelectMenu
{
Placeholder = new PlainText { Text = "Choose an option" },
ActionId = "select_option",
Options = new List<Option>
{
new Option
{
Text = new PlainText { Text = "Option 1" },
Value = "value1"
},
new Option
{
Text = new PlainText { Text = "Option 2" },
Value = "value2"
},
new Option
{
Text = new PlainText { Text = "Option 3" },
Value = "value3"
}
}
}
}
}
};
Forms with Input Elements
With SlackNetBlockBuilder:
var blocks = BlockBuilder.Create()
.AddHeader(new PlainText("Submit Request Form"))
.AddInput<PlainTextInput>("Title", input => input
.ActionId("title_input")
.Placeholder("Enter a title")
.Required())
.AddInput<PlainTextInput>("Description", input => input
.ActionId("description_input")
.Multiline()
.Placeholder("Enter a detailed description")
.Optional())
.AddInput<DatePicker>("Due Date", input => input
.ActionId("due_date_input")
.Placeholder("Select a date")
.Optional())
.AddActions(actions => actions
.Button(button => button
.Text("Submit")
.ActionId("submit_form")
.Primary())
.Button(button => button
.Text("Cancel")
.ActionId("cancel_form")))
.Build();
Without SlackNetBlockBuilder (Traditional Approach):
var blocks = new List<Block>
{
new HeaderBlock
{
Text = new PlainText { Text = "Submit Request Form" }
},
new InputBlock
{
Label = new PlainText { Text = "Title" },
Element = new PlainTextInput
{
ActionId = "title_input",
Placeholder = new PlainText { Text = "Enter a title" }
},
Optional = false
},
new InputBlock
{
Label = new PlainText { Text = "Description" },
Element = new PlainTextInput
{
ActionId = "description_input",
Multiline = true,
Placeholder = new PlainText { Text = "Enter a detailed description" }
},
Optional = true
},
new InputBlock
{
Label = new PlainText { Text = "Due Date" },
Element = new DatePicker
{
ActionId = "due_date_input",
Placeholder = new PlainText { Text = "Select a date" }
},
Optional = true
},
new ActionsBlock
{
Elements = new List<IActionElement>
{
new Button
{
Text = new PlainText { Text = "Submit" },
ActionId = "submit_form",
Style = ButtonStyle.Primary
},
new Button
{
Text = new PlainText { Text = "Cancel" },
ActionId = "cancel_form"
}
}
}
};
Updating Messages
With SlackNetBlockBuilder:
// When handling an interaction payload
public async Task HandleInteraction(InteractionPayload payload)
{
// Create a new block builder from the existing blocks
var updatedBlocks = BlockBuilder.From(payload.Message.Blocks)
// Remove a specific block by ID
.Remove("status_block")
// Add a new status section
.AddSection(section => section
.BlockId("status_block")
.Text("✅ Request approved!"))
.Build();
// Update the message
await slackApi.Chat.Update(new MessageUpdate
{
Channel = payload.Channel.Id,
Ts = payload.Message.Ts,
Blocks = updatedBlocks
});
}
Without SlackNetBlockBuilder (Traditional Approach):
// When handling an interaction payload
public async Task HandleInteraction(InteractionPayload payload)
{
// Get the existing blocks
var existingBlocks = payload.Message.Blocks;
// Create a new list of blocks
var updatedBlocks = new List<Block>();
// Copy all blocks except the status block
foreach (var block in existingBlocks)
{
if (block.BlockId != "status_block")
{
updatedBlocks.Add(block);
}
}
// Add a new status section
updatedBlocks.Add(new SectionBlock
{
BlockId = "status_block",
Text = new PlainText { Text = "✅ Request approved!" }
});
// Update the message
await slackApi.Chat.Update(new MessageUpdate
{
Channel = payload.Channel.Id,
Ts = payload.Message.Ts,
Blocks = updatedBlocks
});
}
Validation
The library includes validation to ensure that the blocks you create are valid according to Slack's Block Kit guidelines. This allows errors to be caught locally without requiring a network call. Right now, validation throws InvalidOperationException, but a future release may use a dedicated exception type for validation errors.
Example:
var blocks = new BlockBuilder()
// throws InvalidOperationException
// Each field in a section block can have at most 3000 characters
.AddSection("A string with more than 3000 characters")
.Build();
Constraints on blocks are referenced from Slack's Block Kit documentation.
Further Examples
For more details see the documentation on GitHub Pages or check out the examples directory in the repository.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- JetBrains.Annotations (>= 2024.3.0)
- SlackNet (>= 0.16.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
0.6.2-beta | 108 | 7/16/2025 |
0.5.1-beta | 112 | 7/16/2025 |
0.0.2-alpha | 53 | 5/2/2025 |
0.0.1-alpha | 64 | 5/2/2025 |