CSharpier: a printer for C#, with almost no settings
CSharpier is an opinionated code formatter for c#.
At a glance
- What is it?
- CSharpier does not reflow your text, it parses your code and prints it again. That distinction is the whole design: once the printer works from a syntax tree rather than from lines, every formatting decision can be made without negotiation, which is how a formatter can be opinionated and still be adopted by a team that disagrees with it about style.
- Who is it for?
- Adopt CSharpier if your team will accept its printing rules, because the deliberate answer to formatting disagreement is to remove the decision rather than add a switch, and no configuration will widen that. Wire the check into your pipeline rather than only the editor, since the editor integration and the pre-commit hook are both bypassable and a build step is the one that holds.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It parses, then it prints again
Almost every formatter works on lines. It measures a line, finds that it is too long, and breaks it somewhere. That approach is cheap, it works on any language with braces, and it has a specific failure mode: when two rules disagree about where to break, you get a rule that has to be special-cased, and the special cases accumulate.
CSharpier takes the other route. It parses your code and re-prints it using its own rules, so the input text is only the source of the tree. The output is a function of the syntax and the printer's decisions, not of what your file happened to look like. The README's own example shows the property: a call with three long string literals comes out broken across lines with a trailing comma.
public class ClassName {
public void CallMethod() {
this.LongUglyMethod("1234567890", "abcdefghijklmnopqrstuvwxyz", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}
}public class ClassName
{
public void CallMethod()
{
this.LongUglyMethod(
"1234567890",
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
);
}
}The decision to break was made once, by the printer, from the tree. There is no line-length setting because there is nothing to configure a line length against; the printer decides what fits.
The other half of that sentence is the limitation. A printer can only emit what its parser modelled, so anything the C# grammar does not fully represent is at risk of being printed in a shape you did not write. That is the boundary to understand before you run a parse-and-reprint formatter across a codebase with unusual generics, attributes or preprocessor work in it, and it is why the sensible adoption pattern is to run it on a branch and read the diff.
XML is in scope for the same reason it is easy: an XML document has a parse tree too, so the same printer machinery applies without needing a second language-specific formatter.
The option philosophy, borrowed by name
The README does not present CSharpier's small configuration surface as an omission. It cites prettier's option philosophy as the source and says outright that there are no plans to add more options.
That philosophy is easy to state and easy to forget. Every option is a permanent cost paid by everyone who uses the tool, because it is a thing that can be set wrongly, that differs between projects, that has to be documented, and that the formatter's own tests have to cover in combination. The arithmetic favours a small surface, and the answer to a formatting disagreement becomes not a new flag but a different rule.
For C#, the consequences are concrete. You are not choosing an indentation width here; that belongs to the editor tooling and lives in an editor configuration file. You are not choosing a brace style. You are not choosing whether a single-attribute class stays on one line. Those decisions belong to the printer.
And the repository demonstrates the boundary rather than just asserting it. At the top level there is an editor configuration file and a tool-specific ignore file, side by side with the source. That is the shape of the compromise: editor-level preferences are yours, formatter-level decisions are not, and the tool tells you to put your preferences in the file designed for them.
If your team's conventions conflict with the printer's rules, there is no configuration that will express it. The honest options are to accept the rules or to use a different tool. That is a worse answer than a flag, and it is the right one for a tool whose entire value proposition is that everyone gets the same output without discussing it.
It formats its own repository
The file list at the top of this repository is short, and three entries in it are the same message.
There is a tool-specific ignore file, there is an editor configuration file, and there is a directory for Git hooks. Together they say the project runs its own formatter over its own source, with the hook that does it committed to the repository rather than configured per machine.
For a formatter that is the only real test that matters. The unit tests can prove that a given input prints a given output, and that is worth doing, but the interesting failures are the ones nobody wrote a test for: an unusual construct in a real file that the printer shapes in a way the author did not expect. A project that runs the tool over itself in a hook finds those immediately, because the pull request that introduces them is the diff.
The ignore file has a second job, which is generic but worth noting because it is easy to forget. It exists so that generated files are excluded, and a formatter that has to be told to skip a directory is a formatter you will eventually run over something you meant to exclude.
The rest of the tree is a modern .NET layout, and two entries are worth naming. There is a solution file in the newer XML format alongside a Rider settings file, which together say which toolchain the project is developed against. And there are two directory-level build property files, one for shared settings and one for package versions, which is central package management: one declared version per dependency across the whole repository rather than a version decided per project. For anyone copying layout from this repository, that is the more valuable pattern of the two.
Editor, hook, pipeline: only one of them holds
The README lists three ways to run CSharpier and they are not equivalent.
The first is on save in the editor, which means the formatting happens when the author is looking at the result and can undo it. The second is as a pre-commit hook, which means it happens before anything is recorded, and which is the point at which the author still has context about what they were doing. The third is a check in a continuous integration pipeline, which means it happens after the code has been merged and reviewed.
Only the third one cannot be bypassed, and that is why the ordering matters more than it looks. An editor integration depends on everyone installing the extension and enabling it. A pre-commit hook can be skipped with a flag, and most people skip it exactly when the hook is fighting them, which is precisely the moment when a consistent format is most valuable. A pipeline check has no bypass, so a repository that adopts this formatter and only wires the third one has made a decision rather than made a suggestion.
The install is a .NET global tool, which is the right shape for this: it goes through the platform's own tool mechanism, so it upgrades with the rest of the tooling and does not need a package manager of its own.
dotnet tool install -g csharpierAnd formatting a tree is one command with a path:
csharpier format .The README also links a web playground, which is a genuinely useful escape hatch for this particular class of tool. When you are deciding whether to adopt an opinionated formatter, the question is whether you can live with its output, and a playground that shows you its output for your own code answers that faster than any amount of documentation about its rules.
The container builds the playground, not the tool
The Dockerfile is worth reading for what it reveals about the project, and what it reveals is that there is a web application in this repository alongside the formatter.
The entry point is the playground assembly, and the runtime stage is an ASP.NET base image rather than a console runtime:
FROM mcr.microsoft.com/dotnet/aspnet:11.0-preview AS base
WORKDIR /app
ENV ASPNETCORE_URLS=http://+:80
EXPOSE 80The build stage is an SDK image, and it installs Node before it does anything else, from the NodeSource repository with the signing key dearmored into a keyring:
&& NODE_MAJOR=22 \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" \
| tee /etc/apt/sources.list.d/nodesource.list \A formatter written in one language does not need Node. So the Node toolchain is there for the playground's client application, whose manifest and lock file are copied in and installed separately from the .NET restore.
The layering is deliberate and worth copying. Only the shared build property files, the package configuration and the two project files needed for restore are copied first, so the expensive dependency restore is cached and not invalidated by every source edit. Then the client dependencies, then the source, then a publish stage, and finally the copy from publish into the runtime base.
Two observations for anyone evaluating the project. The images are .NET 11 preview builds, which is a choice about being current rather than about stability, and the container is the playground, so if you were expecting a containerised formatter you will not find one here. The formatter ships as a .NET global tool, and the website has its own separate Dockerfile.
Maintenance, funding, and the scope it does not cover
The release history is a healthy one for a tool: version 1.2.5 in December 2025, 1.2.6 in February 2026, and 1.3.0 in June 2026, so the project is past its first major version and shipping. The last push was on 2026-09-26 and the repository is not archived. The licence is MIT.
The sponsorship list is worth reading for a different reason than funding usually gets. A formatter is unglamorous infrastructure: nobody writes a blog post about it, it has no conference talks, and its only audience is developers who already decided to use it. Projects in that position live or die on sponsorship, and the roster here includes a corporate open source fund and a documentation platform company, which is a reasonable signal that this one has support rather than enthusiasm.
The scope boundary is the thing to be clear about. It formats C# and XML. Nothing else.
That is not a criticism, it is a fact with consequences. A modern repository contains TypeScript, SQL, Markdown, YAML, JSON and generated code, and this tool handles none of them. So adopting it does not remove your formatting discussion, it removes it from one language and leaves it for the others, and you end up with two formatters whose ideas about line length and trailing commas may not agree. That is a real cost, and the mitigation is the same as for any formatter: pick them once, wire both into the same pipeline check, and stop having the conversation.
The other boundary is the one from earlier, restated because it is the one that bites: this tool re-prints from a parse tree, so anything the grammar does not model is printed according to the printer's idea of it rather than yours. Run it on a branch and read the diff the first time, on a codebase with preprocessor directives and heavy generic code, before you let a pipeline check start failing builds.
Editorial conclusion
Adopt CSharpier if your team will accept its printing rules, because the deliberate answer to formatting disagreement is to remove the decision rather than add a switch, and no configuration will widen that. Wire the check into your pipeline rather than only the editor, since the editor integration and the pre-commit hook are both bypassable and a build step is the one that holds. Plan for a second formatter for everything it does not cover, since it handles C# and XML only, and read the parse-and-reprint boundary before you trust it on syntax the parser might not model.
Frequently asked questions
What is CSharpier and how does it format code?
CSharpier is an opinionated formatter for C# and XML. It parses your code and re-prints it using its own rules rather than reflowing text line by line, and the printing process was ported from prettier and has evolved since. It also formats XML, since an XML document has a parse tree in the same way.
How do I install and run CSharpier?
Install it as a .NET global tool with `dotnet tool install -g csharpier`, then run `csharpier format .` to format a directory and everything under it. It can also run on save in your editor, as a pre-commit hook, or as a check in a continuous integration pipeline.
Why does CSharpier have so few configuration options?
It follows prettier's option philosophy, which treats every option as a permanent cost paid by all users, and the project states it has no plans to add more. Editor-level preferences such as indentation belong in your editor configuration file, while formatting decisions belong to the printer.
Does CSharpier format anything other than C#?
It formats C# and XML. Other languages and file types in a repository, including TypeScript, SQL, Markdown, YAML and JSON, are not covered, so a repository adopting it will still need another formatter for those.
Why is there a Dockerfile that installs Node in a C# project?
Because the container builds the web playground rather than the formatter. It uses an ASP.NET base image for the playground assembly and installs Node 22 from the NodeSource repository to build the playground's client application before the .NET publish step.
How does CSharpier handle a codebase with unusual C# syntax?
It re-prints from a parse tree, so anything the grammar does not fully represent is printed according to the printer's rules rather than your original layout. The README does not describe exemptions for particular constructs, so the practical approach is to run it on a branch and read the diff before enabling a pipeline check.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/belav-csharpier)