Browse by type
A small C library that portably invokes native file open, folder select and file save dialogs. Write dialog code once and have it pop up native dialogs on all supported platforms. Avoid linking large dependencies like wxWidgets and Qt.
This library is based on Michael Labbe's Native File Dialog (mlabbe/nativefiledialog).
Features:
C/C++ Source files (*.c;*.cpp) instead of (*.c;*.cpp)) on platforms that support itUntitled.c)wchar_t) support on WindowsIFileDialog on Windowsunique_ptr auto-freeing semantics and optional parameters, for those using this library from C++Comparison with original Native File Dialog:
The friendly names feature is the primary reason for breaking API compatibility with Michael Labbe's library (and hence this library probably will never be merged with it). There are also a number of tweaks that cause observable differences in this library.
Features added in Native File Dialog Extended:
wchar_t) support on Windowsunique_ptr auto-freeing semantics and optional parametersThere is also significant code refractoring, especially for the Windows implementation.
The wiki keeps track of known language bindings and known popular projects that depend on this library.
#include <nfd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
NFD_Init();
nfdu8char_t *outPath;
nfdu8filteritem_t filters[2] = { { "Source code", "c,cpp,cc" }, { "Headers", "h,hpp" } };
nfdopendialogu8args_t args = {0};
args.filterList = filters;
args.filterCount = 2;
nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args);
if (result == NFD_OKAY)
{
puts("Success!");
puts(outPath);
NFD_FreePathU8(outPath);
}
else if (result == NFD_CANCEL)
{
puts("User pressed cancel.");
}
else
{
printf("Error: %s\n", NFD_GetError());
}
NFD_Quit();
return 0;
}
The U8/u8 in NFDe refer to the API for UTF-8 characters (char), which most consumers probably want. An N/n version is also available, which uses the native character type (wchar_t on Windows and char on other platforms).
For the full list of arguments that you can set on the args struct, see the "All Options" section below.
If you are using a platform abstraction framework such as SDL or GLFW, also see the "Usage with a Platform Abstraction Framework" section below.

If your project uses CMake, simply add the following lines to your CMakeLists.txt:
add_subdirectory(path/to/nativefiledialog-extended)
target_link_libraries(MyProgram PRIVATE nfd)
Make sure that you also have the needed dependencies.
When included as a subproject, sample programs are not built and the install target is disabled by default.
Add -DNFD_BUILD_TESTS=ON to build sample programs and -DNFD_INSTALL=ON to enable the install target.
If you want to build the standalone static library, execute the following commands (starting from the project root directory):
For GCC and Clang:
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
For MSVC:
mkdir build
cd build
cmake ..
cmake --build . --config Release
The above commands will make a build directory,
and build the project (in release mode) there.
If you are developing NFDe, you may want to do -DCMAKE_BUILD_TYPE=Debug/--config Debug
to build a debug version of the library instead.
When building as a standalone library, sample programs are built and the install target is enabled by default.
Add -DNFD_BUILD_TESTS=OFF to disable building sample programs and -DNFD_INSTALL=OFF to disable the install target.
On Linux, if you want to use the Flatpak desktop portal instead of GTK, add -DNFD_PORTAL=ON. (Otherwise, GTK will be used.) See the "Usage" section below for more information.
See the CI build file for some example build commands.
Recent versions of Visual Studio have CMake support built into the IDE. You should be able to "Open Folder" in the project root directory, and Visual Studio will recognize and configure the project appropriately. From there, you will be able to set configurations for Debug vs Release, and for x86 vs x64. For more information, see the Microsoft Docs page. This has been tested to work on Visual Studio 2019, and it probably works on Visual Studio 2017 too.
src/include to your include search path.nfd.lib or nfd_d.lib to the list of static libraries to link against (for release or debug, respectively).build/<debug|release>/<arch> to the library search path.Make sure libgtk-3-dev is installed on your system.
Make sure libdbus-1-dev is installed on your system.
On macOS, add AppKit and UniformTypeIdentifiers to the list of frameworks.
On Windows (both MSVC and MinGW), ensure you are building against ole32.lib, uuid.lib, and shell32.lib.
To open a dialog, you set options on a struct and then pass that struct to an NFDe function, e.g.:
nfdopendialogu8args_t args = {0};
args.filterList = filters;
args.filterCount = 2;
nfdresult_t result = NFD_OpenDialogU8_With(&outPath, &args);
All options are optional and may be set individually (zero initialization sets all options to reasonable defaults), except for filterList and filterCount which must be either both set or both left unset.
Future versions of NFDe may add additional options to the end of the arguments struct without bumping the major version number, so to ensure backward API compatibility, you should not assume that the struct has a specific length or number of fields. You may assume that zero-initialization of the struct will continue to set all options to reasonable defaults, so assigning {0} to the struct is acceptable. For those building shared libraries of NFDe, backward ABI compatibility is ensured by an internal version index (NFD_INTERFACE_VERSION), which is expected to be transparent to consumers.
OpenDialog/OpenDialogMultiple:
typedef struct {
const nfdu8filteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdopendialogu8args_t;
SaveDialog:
typedef struct {
const nfdu8filteritem_t* filterList;
nfdfiltersize_t filterCount;
const nfdu8char_t* defaultPath;
const nfdu8char_t* defaultName;
nfdwindowhandle_t parentWindow;
} nfdsavedialogu8args_t;
PickFolder/PickFolderMultiple:
typedef struct {
const nfdu8char_t* defaultPath;
nfdwindowhandle_t parentWindow;
} nfdpickfolderu8args_t;
filterList and filterCount: Set these to customize the file filter (it appears as a dropdown menu on Windows and Linux, but simply hides files on macOS). Set filterList to a pointer to the start of the array of filter items and filterCount to the number of filter items in that array. See the "File Filter Syntax" section below for details.defaultPath: Set this to the default folder that the dialog should open to (see the "Platform-specific Quirks" section for more details about the behaviour of this option on Windows).defaultName: (For SaveDialog only) Set this to the file name that should be pre-filled on the dialog.parentWindow: Set this to the native window handle of the parent of this dialog. See the "Usage with a Platform Abstraction Framework" section for details. It is also possible to pass a handle even if you do not use a platform abstraction framework.See the test directory for example code (both C and C++).
If you turned on the option to build the test directory (-DNFD_BUILD_TESTS=ON), then build/bin will contain the compiled test programs.
There is also an SDL2 example, which needs to be enabled separately with -DNFD_BUILD_SDL2_TESTS=ON. It requires SDL2 to be installed on your machine.
Compiled examples (including the SDL2 example) are also uploaded as artefacts to GitHub Actions, and may be downloaded from there.
Files can be filtered by file extension groups:
nfdu8filteritem_t filters[2] = { { "Source code", "c,cpp,cc" }, { "Headers", "h,hpp" } };
A file filter is a pair of strings comprising the friendly name and the specification (multiple file extensions are comma-separated).
A list of file filters can be passed as an argument when invoking the library.
A wildcard filter is always added to every dialog.
Note: On macOS, the file dialogs do not have friendly names and there is no way to switch between filters, so the filter specifications are combined (e.g. "c,cpp,cc,h,hpp"). The filter specification is also never explicitly shown to the user. This is usual macOS behaviour and users expect it.
Note 2: You must ensure that the specification string is non-empty and that every file extension has at least one character. Otherwise, bad things might ensue (i.e. undefined behaviour).
Note 3: On Linux, the file extension is appended (if missing) when the user presses down the "Save" button. The appended file extension will remain visible to the user, even if an overwrite prompt is shown and the user then presses "Cancel".
Note 4: Linux is designed for case-sensitive file filters, but this is perhaps not what most users expect. A simple hack is used to make filters case-insensitive. To get case-sensitive filtering, set the NFD_CASE_SENSITIVE_FILTER build option to ON.
A file open dialog that supports multiple selection produces a PathSet, which is a thin abstraction over the platform-specific collection. There are two ways to iterate over a PathSet:
This method does array-like access on the PathSet, and is the easiest to use. However, on certain platforms (Linux, and possibly Windows), it takes O(N2) time in total to iterate the entire PathSet, because the underlying platform-specific implementation uses a linked list.
See test_opendialogmultiple.c.
This method uses an enumerator object to iterate the paths in the PathSet. It is guaranteed to take O(N) time in total to iterate the entire PathSet.
See test_opendialogmultiple_enum.c.
This API is experimental, and subject to change.
You can define the following macros before including nfd.h/nfd.hpp:
NFD_NATIVE: Define this before including nfd.h to make non-suffixed function names and typedefs (e.g. NFD_OpenDialog) aliases for the native functions (e.g. NFD_OpenDialogN) instead of aliases for the UTF-8 functions (e.g. NFD_OpenDialogU8). This macro does not affect the C++ wrapper nfd.hpp.NFD_THROWS_EXCEPTIONS: (C++ only) Define this before including nfd.hpp to make NFD::Guard construction throw std::runtime_error if NFD_Init fails. Otherwise, there is no way to detect failure in NFD::Guard construction.Macros that might be defined by nfd.h:
NFD_DIFFERENT_NATIVE_FUNCTIONS: Defined if the native and UTF-8 versions of functions are different (i.e. compiling for Windows); not defined otherwise. If NFD_DIFFERENT_NATIVE_FUNCTIONS is not defined, then the UTF-8 versions of functions are aliases for the native versions. This might be useful if you are writing a function that wants to provide overloads depending on whether the native functions and UTF-8 functions are the same. (Nativ$ claude mcp add nativefiledialog-extended \
-- python -m otcore.mcp_server <graph>