Introduction
Unreal Engine provides several tools for organizing and managing data within a game.
One such tool is the Data Table, which allows developers to define and store data in a structured format that can be easily accessed and modified.
In this article, we’ll explore the basics of using data tables in Unreal Engine and demonstrate how they can be used to manage game data.
What Are Data Tables in Unreal Engine?
Data Tables are a way of organizing and managing data within Unreal Engine. They are essentially spreadsheets that store data in rows and columns, with each row representing a specific object or instance of data.
Data Tables can be used to store any type of data that can be represented in a table format, including game settings, level layouts, character statistics, and more.
Unreal also has other assets like Data Assets, but we may check that out in another article in the future.
Define a row struct
Before you can create a Data Table you need a row struct: a struct that describes the columns. Every row in the table is one instance of this struct, and every property on the struct becomes a column.
The only hard requirement is that the struct inherits from FTableRowBase. Here is a small one for an item table:
USTRUCT(BlueprintType)
struct FItemTableRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FText DisplayName;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 Cost = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float Weight = 0.f;
};
BlueprintType lets you use the struct, and the rows you read from it, in Blueprint. Once the struct compiles, Unreal offers it in the row-structure list when you create the Data Table.
If you only work in Blueprint you can build the same thing with a Blueprint Structure asset ( right-click in the Content Browser, Blueprint > Structure ). A Data Table will accept it as a row type the same way.
How to Create a Data Table Asset in UE5
To create a new Data Table in Unreal Engine, right-click in the Content Browser and select “Data Table” from the “Miscellaneous” section of the context menu.

Unreal will ask you to pick a Row Structure. This is where your row struct comes in: choose the struct you defined above ( or a Blueprint Structure ), and the table takes its columns from that struct.
Double-click the asset to open the Data Table Editor. One thing that trips people up: you don’t add or edit columns here. The columns are the properties of the row struct, so the only way to add a “Mana” or “Defense” column is to add that property to the struct and recompile. In the editor itself you work with rows, not columns.
How to Add and Edit Rows in a Data Table
Once you have created a Data Table, you can populate it with data. To do this, simply add new rows to the table by clicking the Add Row button in the Data Table Editor.
Each row in the table represents an instance of data, so you’ll want to add a new row for each object or instance of data that you want to store.
For example, if you were creating a Data Table to store character statistics, you might add a row for each character in your game, with the values for each column representing the character’s stats.

Importing from CSV and JSON
You can also skip manual entry and import the whole table from a CSV or JSON file, which is handy when a designer keeps the data in a spreadsheet.
For a CSV, the first column is the row name. Its header can be Name ( or left as --- ), and every other column header has to match a property name on your row struct, spelled the same way. A file for the item struct above would look like this:
Name,DisplayName,Cost,Weight
Sword,Iron Sword,120,3.5
Shield,Wooden Shield,60,5.0
JSON works too, as an array of objects where each object has a Name field plus the struct properties:
[
{ "Name": "Sword", "DisplayName": "Iron Sword", "Cost": 120, "Weight": 3.5 },
{ "Name": "Shield", "DisplayName": "Wooden Shield", "Cost": 60, "Weight": 5.0 }
]
To bring a file in, right-click in the Content Browser and choose Import, or use Reimport on an existing Data Table to pull in updated data while keeping the same asset. If a column name in the file doesn’t match a struct property, Unreal skips it and warns you in the Message Log, so check there when a column comes in empty.
How to Read Rows in Blueprint
In Blueprint the node you want is Get Data Table Row. You give it the Data Table and a Row Name, and you get back two things: an exec output that splits into Row Found and Row Not Found, and an Out Row output.
The Out Row is a wildcard that resolves to your row struct once you pick the table, so you drag off it and Break the struct to read the fields ( DisplayName, Cost, Weight ).
The Row Not Found branch matters. If the row name is wrong you get an invalid row and no error, so wire up that path instead of assuming the lookup worked.
Two related nodes are handy: Get Data Table Row Names to loop over every row, and Does Data Table Row Exist when you only need to check for a name.
How to Query Data Tables in C++
Once you have created and populated a Data Table, you can access the data from within your game code.
To do this, you’ll first need to load the Data Table into memory, you do this for instance by storing the Data Table in a class property. But in the end is up to you.
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta=(RowType ="QuodComboData"))
UDataTable* ComboDataTable;
Once the Data Table is loaded, you can access individual rows and columns using the “Get Data Table Row” function.
For example, if you had a Data Table that stored character statistics, you could load the Data Table into memory and then retrieve a specific character’s stats by calling the Get Data Table Row function and passing in the character’s name or ID.
// Get the datatable asset from where you store it
const UDataTable* ComboDataTable = UQuodDynamicAssetLoaderGlobals::GetAssetByName(GetOwner(), AttackDataTableName);
// Find a specific row in the datatable
const FQuodAttackData * attackDataEntry = ComboDataTable->FindRow(TEXT("Attack01"), TEXT("UQuodAttackDataComponent::GetAttackData"));
// Get all rows if you need them all
TArray AllRows;
ComboDataTable->GetAllRows(TEXT("UQuodComboUtils::GetAttackData"), AllRows);
One important thing, as you are seeing by the return type of these functions, is that a Data Table query will always return a struct of the type that the Data Table is based on.
FindRow is a template, so in your own code you pass the row type explicitly, like ComboDataTable->FindRow<FItemTableRow>("Sword", Context). It returns a pointer to the row, or nullptr if the name isn’t there, so null-check it before you use it. The second argument is a context string that shows up in the log when a lookup fails, which makes a missing row much easier to track down. GetAllRows takes the same row type and fills an array of pointers.
Under the hood the lookup is a map keyed by the row name, so FindRow is fast even on a big table. It stays fast as long as the names line up, which is the thing that usually goes wrong.
Common pitfalls
A few things bite people over and over with Data Tables:
- Row name typos. Lookups are by
FNameand they fail silently: a wrong name returns nothing, no crash and no red error. This is the number one reason a Data Table “isn’t working”. - Changing the row struct invalidates data. Renaming a property, changing a type, or reordering fields can drop or reset values in every table built on that struct. After a struct change, open the affected tables and reimport or re-check them, and keep the source CSV or JSON so you can reimport cleanly.
- Names come from the file on import. When you import a CSV or JSON, the row names come from the Name column, so a rename in the spreadsheet creates a new row instead of renaming the old one, and reimporting can leave stale rows behind.
- Data Tables are static, read-only data. Treat a Data Table as constant at runtime. You look data up from it, you don’t write back to it, so use it for definitions ( item stats, combo data, level layouts ), not for save state.
Recap
- A Data Table’s columns come from a row struct that inherits from
FTableRowBase. You can’t add columns in the table editor, you change the struct. - Read a row with Get Data Table Row in Blueprint or FindRow in C++. Both are keyed by the row name, and both fail quietly when the name is wrong, so always handle the not-found case.
- Import from CSV or JSON with
Nameas the first column, and keep the source file so you can reimport after a struct change.
What this doesn’t cover
I stuck to Data Tables here. I didn’t get into Data Assets, Composite Data Tables, or the Data Registry, which fit better once your data has to be composed from several sources or loaded on demand. Async loading and soft references also start to matter once a table gets large, and that is a topic on its own.


