Skip to main content

What You’ll Build

In this quickstart, you’ll create a simple but functional Dalamud plugin that:
  • Registers a custom slash command (/hello)
  • Displays a configuration window using ImGui
  • Sends messages to the game chat
  • Demonstrates proper plugin lifecycle management
By the end, you’ll have a solid foundation for building more complex plugins.
This guide assumes you’ve already completed the Installation setup. If not, do that first!

Step 1: Create the Plugin Class

Every Dalamud plugin must implement the IDalamudPlugin interface. This is the entry point for your plugin.
1

Create the Main Plugin File

Create a new file called Plugin.cs with the following code:
Plugin.cs
using Dalamud.Game.Command;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;

namespace HelloWorldPlugin;

public class Plugin : IDalamudPlugin
{
    private readonly IDalamudPluginInterface pluginInterface;
    private readonly ICommandManager commandManager;
    private readonly IChatGui chatGui;
    private readonly WindowSystem windowSystem;
    private readonly ConfigWindow configWindow;

    public Plugin(
        IDalamudPluginInterface pluginInterface,
        ICommandManager commandManager,
        IChatGui chatGui)
    {
        this.pluginInterface = pluginInterface;
        this.commandManager = commandManager;
        this.chatGui = chatGui;

        // Create window system
        this.windowSystem = new WindowSystem("HelloWorldPlugin");
        this.configWindow = new ConfigWindow();
        this.windowSystem.AddWindow(this.configWindow);

        // Register command
        this.commandManager.AddHandler("/hello", new CommandInfo(OnCommand)
        {
            HelpMessage = "Opens the Hello World plugin configuration."
        });

        // Hook up UI drawing
        this.pluginInterface.UiBuilder.Draw += this.windowSystem.Draw;
        this.pluginInterface.UiBuilder.OpenConfigUi += this.ToggleConfigWindow;
    }

    public void Dispose()
    {
        // Clean up resources
        this.commandManager.RemoveHandler("/hello");
        this.pluginInterface.UiBuilder.Draw -= this.windowSystem.Draw;
        this.pluginInterface.UiBuilder.OpenConfigUi -= this.ToggleConfigWindow;
        this.windowSystem.RemoveAllWindows();
    }

    private void OnCommand(string command, string args)
    {
        this.chatGui.Print("Hello from your plugin!");
        this.ToggleConfigWindow();
    }

    private void ToggleConfigWindow()
    {
        this.configWindow.Toggle();
    }
}
2

Understand the Code

Let’s break down what’s happening:
public Plugin(
    IDalamudPluginInterface pluginInterface,
    ICommandManager commandManager,
    IChatGui chatGui)
Dalamud automatically injects services into your constructor. These are the APIs you’ll use to interact with the game.
  • IDalamudPluginInterface: Core interface for plugin metadata, configuration, and UI
  • ICommandManager: Registers custom slash commands
  • IChatGui: Sends messages to the game chat
this.commandManager.AddHandler("/hello", new CommandInfo(OnCommand)
{
    HelpMessage = "Opens the Hello World plugin configuration."
});
This registers the /hello command. When a player types it, the OnCommand method is called.
this.windowSystem = new WindowSystem("HelloWorldPlugin");
this.pluginInterface.UiBuilder.Draw += this.windowSystem.Draw;
The WindowSystem manages all windows in your plugin. You hook into Dalamud’s draw loop to render your UI.
public void Dispose()
{
    this.commandManager.RemoveHandler("/hello");
    this.pluginInterface.UiBuilder.Draw -= this.windowSystem.Draw;
    // ...
}
Critical: Always clean up registered commands, event handlers, and windows in Dispose(). This allows hot-reloading during development.

Step 2: Create the Configuration Window

Now let’s create the UI window that appears when you run /hello.
1

Create the Window Class

Create a new file called ConfigWindow.cs:
ConfigWindow.cs
using System.Numerics;
using Dalamud.Interface.Windowing;
using ImGuiNET;

namespace HelloWorldPlugin;

public class ConfigWindow : Window
{
    private string inputText = string.Empty;
    private int counter = 0;

    public ConfigWindow() : base("Hello World Configuration###HelloWorldConfig")
    {
        // Set window properties
        this.Size = new Vector2(400, 300);
        this.SizeCondition = ImGuiCond.FirstUseEver;
    }

    public override void Draw()
    {
        ImGui.Text("Welcome to your first Dalamud plugin!");
        ImGui.Spacing();

        // Input text field
        ImGui.Text("Enter some text:");
        ImGui.InputText("##input", ref this.inputText, 100);
        ImGui.Spacing();

        // Button with counter
        if (ImGui.Button($"Click me! ({this.counter})"))
        {
            this.counter++;
        }
        ImGui.Spacing();

        // Display entered text
        if (!string.IsNullOrEmpty(this.inputText))
        {
            ImGui.TextColored(new Vector4(0, 1, 0, 1), $"You entered: {this.inputText}");
        }
    }
}
2

Understand the Window Code

this.Size = new Vector2(400, 300);
this.SizeCondition = ImGuiCond.FirstUseEver;
Set the initial window size. ImGuiCond.FirstUseEver means the size is only set on first display—users can resize it afterward.
public override void Draw()
{
    ImGui.Text("Welcome to your first Dalamud plugin!");
    ImGui.InputText("##input", ref this.inputText, 100);
    // ...
}
The Draw() method is called every frame. Use ImGui functions to render UI elements:
  • ImGui.Text() - Display text
  • ImGui.InputText() - Text input field
  • ImGui.Button() - Clickable button
  • ImGui.TextColored() - Colored text

Step 3: Build and Test

1

Build the Project

Open a terminal in your plugin directory and run:
dotnet build
You should see output like:
Build succeeded.
    0 Warning(s)
    0 Error(s)
If you get compilation errors, double-check that:
  • The DalamudLibPath in your .csproj is correct
  • You’ve installed the .NET 8.0 SDK
  • All using statements are present
2

Load the Plugin In-Game

  1. Launch FFXIV via XIVLauncher (not the official launcher)
  2. Press Escape and click Plugin Installer, or type /xlplugins
  3. Go to the Dev Plugins tab
  4. If your plugin isn’t listed, click Add and browse to bin\Debug\
  5. Click Load next to your plugin
3

Test the Command

In the game chat, type:
/hello
You should see:
  • A chat message: “Hello from your plugin!”
  • Your configuration window appears with a text input and clickable button
4

Interact with the UI

Try these interactions:
  • Type something in the text input field
  • Click the “Click me!” button and watch the counter increment
  • Close and reopen the window with /hello
  • Open plugin settings via /xlplugins → Click the settings icon next to your plugin

Step 4: Add Configuration Persistence

Let’s save the counter value so it persists between game sessions.
1

Create a Configuration Class

Create Configuration.cs:
Configuration.cs
using Dalamud.Configuration;
using System;

namespace HelloWorldPlugin;

[Serializable]
public class Configuration : IPluginConfiguration
{
    public int Version { get; set; } = 0;
    public int SavedCounter { get; set; } = 0;

    public void Save(IDalamudPluginInterface pluginInterface)
    {
        pluginInterface.SavePluginConfig(this);
    }
}
2

Update the Plugin Class

Modify Plugin.cs to load and save configuration:
Plugin.cs
public class Plugin : IDalamudPlugin
{
    private readonly Configuration config;
    // ... other fields ...

    public Plugin(
        IDalamudPluginInterface pluginInterface,
        ICommandManager commandManager,
        IChatGui chatGui)
    {
        this.pluginInterface = pluginInterface;
        this.commandManager = commandManager;
        this.chatGui = chatGui;

        // Load configuration
        this.config = pluginInterface.GetPluginConfig() as Configuration ?? new Configuration();

        // Create window with config
        this.windowSystem = new WindowSystem("HelloWorldPlugin");
        this.configWindow = new ConfigWindow(this.config, pluginInterface);
        this.windowSystem.AddWindow(this.configWindow);

        // ... rest of initialization ...
    }

    // ... rest of class ...
}
3

Update the Window Class

Modify ConfigWindow.cs to use the configuration:
ConfigWindow.cs
public class ConfigWindow : Window
{
    private readonly Configuration config;
    private readonly IDalamudPluginInterface pluginInterface;
    private string inputText = string.Empty;

    public ConfigWindow(Configuration config, IDalamudPluginInterface pluginInterface) 
        : base("Hello World Configuration###HelloWorldConfig")
    {
        this.config = config;
        this.pluginInterface = pluginInterface;
        this.Size = new Vector2(400, 300);
        this.SizeCondition = ImGuiCond.FirstUseEver;
    }

    public override void Draw()
    {
        ImGui.Text("Welcome to your first Dalamud plugin!");
        ImGui.Spacing();

        ImGui.Text("Enter some text:");
        ImGui.InputText("##input", ref this.inputText, 100);
        ImGui.Spacing();

        // Button with saved counter
        if (ImGui.Button($"Click me! ({this.config.SavedCounter})"))
        {
            this.config.SavedCounter++;
            this.config.Save(this.pluginInterface);
        }
        ImGui.Spacing();

        if (!string.IsNullOrEmpty(this.inputText))
        {
            ImGui.TextColored(new Vector4(0, 1, 0, 1), $"You entered: {this.inputText}");
        }
    }
}
4

Test Persistence

  1. Rebuild: dotnet build
  2. Unload and reload your plugin in /xlplugins
  3. Click the button a few times
  4. Completely close and restart the game
  5. Load your plugin again—the counter should remember its value!
Configuration files are stored at:
%AppData%\XIVLauncher\pluginConfigs\YourPluginInternalName\

What’s Next?

Congratulations! You’ve built a fully functional Dalamud plugin. Here’s where to go from here:

Plugin Development Guide

Learn advanced plugin patterns and best practices

UI & ImGui

Master custom UI development with ImGui

Game Integration

Access player state, targets, and game data

IPC System

Enable inter-plugin communication

Common Patterns

Here are some useful patterns you’ll use frequently:

Accessing Player Information

public Plugin(IClientState clientState)
{
    var player = clientState.LocalPlayer;
    if (player != null)
    {
        var name = player.Name.TextValue;
        var level = player.Level;
        var currentHP = player.CurrentHp;
    }
}

Handling Chat Messages

public Plugin(IChatGui chatGui)
{
    chatGui.ChatMessage += OnChatMessage;
}

private void OnChatMessage(
    XivChatType type,
    int timestamp,
    ref SeString sender,
    ref SeString message,
    ref bool isHandled)
{
    var messageText = message.TextValue;
    if (messageText.Contains("hello"))
    {
        chatGui.Print("I saw you say hello!");
    }
}

Querying Game Data

public Plugin(IDataManager dataManager)
{
    var itemSheet = dataManager.GetExcelSheet<Item>();
    var potion = itemSheet?.GetRow(4564); // Potion item ID
    if (potion != null)
    {
        var itemName = potion.Name.ToString();
    }
}

Troubleshooting

Check the logs:Type /xllog in-game or check:
%AppData%\XIVLauncher\dalamud.log
Common issues:
  • Missing IDalamudPlugin implementation
  • Constructor parameter types don’t match available services
  • Exception thrown during initialization
Make sure:
  • The command starts with /
  • You called AddHandler() before the plugin finished loading
  • You’re not removing the handler too early
  • The command name doesn’t conflict with existing commands
Verify:
  • UiBuilder.Draw event is subscribed
  • windowSystem.Draw() is called in the handler
  • Window’s IsOpen property is set to true
  • No exceptions are being thrown in Draw()
For more help, join the Dalamud Discord and ask in the #plugin-dev channel!

Build docs developers (and LLMs) love