A sales rep selects “United States” in the Country field, but the City lookup still shows entries from 30 other countries. The rep scrolls, guesses, and picks the wrong city. The order gets shipped to the wrong warehouse.
This happens when lookup fields don’t talk to each other.
In Dynamics 365 CRM, lookups pull from entire entity datasets by default. That creates clutter, slows down users, and invites data errors. Filtering lookup fields in Dynamics 365 solves this by making one lookup respond to another.
This guide walks through the technical setup in detail. Let’s get started!
Key Takeaways
- Lookup fields in Dynamics 365 CRM connect related entities like Accounts, Contacts, and Opportunities for better data relationships
- Unfiltered lookups can cause clutter, confusion, and errors when users select unrelated records
- You can filter lookup fields using Related Records Filtering (no-code) or JavaScript filtering (code-based), depending on complexity
- Testing and event configuration are key, especially enabling OnLoad and OnChange handlers in form properties
- Common issues include missing relationships, incorrect schema names, and empty results, which can be fixed through careful setup and validation
What are Lookup Fields in Dynamics 365 CRM?
Lookup fields in Microsoft Dynamics 365 CRM are form controls that link one record to another by referencing a related entity. They create relationships between data tables, like connecting a Contact to an Account or linking an Opportunity to a specific Product.
When users click a lookup field, they see a searchable list of records from the target entity. Selecting an entry establishes a connection that maintains referential integrity across your CRM database.
How Lookup Fields Work
Lookup fields function as relationship bridges between entities. They store the GUID (Globally Unique Identifier) of the related record, not the actual data. This maintains database normalization and ensures updates to the referenced record automatically reflect wherever that lookup appears.
These are the components:
- Data storage: The lookup field stores only the related record’s GUID. Display values, like account name or contact email, come from the target entity through the relationship.
- Query behavior: When users open a lookup dropdown, Dynamics 365 executes a query against the target entity. By default, this returns all active records matching the configured view.
- Relationship types: Lookups support 1:N (one-to-many), N:1 (many-to-one), and N:N (many-to-many) relationships. Each type determines how records connect and whether the relationship appears on both entities’ forms.
- View configuration: Administrators control which columns appear in lookup dropdowns by customizing lookup views. This affects usability but doesn’t filter the underlying dataset.
Common Use Cases for Lookup Relationships
Lookup fields power core CRM workflows by connecting related business data. Common use cases include:
Linking Contacts to Accounts
Sales teams associate people with organizations through the parentcustomerid lookup. This enables account-level reporting, tracks communication history, and maintains organizational hierarchies across customer relationships.
Associating Opportunities With Products
Revenue tracking depends on product lookups that connect deals to catalog items. This relationship calculates projected revenue, tracks win rates by category, and generates accurate sales forecasts for pipeline management.
Connecting Cases to Knowledge Articles
Support agents link solution documents directly to tickets during resolution. This creates feedback loops showing which articles solve problems effectively and which need content updates or retirement.
Relating Tasks to Parent Records
Activity lookups maintain context across workflows. Tasks linked to opportunities organize sales follow-ups. The same task type linked to case tracks supports commitments. The lookup determines where activities appear in timelines and how they roll up in reports.
Cascading Entity Relationships
Complex data models use multiple lookup layers. Projects link to Accounts. Tasks link to Projects. Resources link to Tasks. Each lookup maintains hierarchy, enabling managers to analyze workload across accounts, projects, or team members through relationship-based filtering in CRM forms.
Methods to Filter Lookup Fields
Dynamics 365 offers two paths for implementing filtered lookup in Dynamics 365 CRM:
1. No-Code Filtering Using Related Records

Native filtering leverages existing entity relationships to automatically restrict lookup options. This method works when one entity has a direct relationship to another, like Contacts linked to Accounts through the parentcustomerid field.
Step #1: Verify the Entity Relationship Exists
Before configuring filters, you need to confirm that the underlying relationship connects your entities properly.
Open your solution in Power Apps (make.powerapps.com) and navigate to Tables. The entity containing the lookup you want to filter should appear in your list. Click on Relationships to view all connections.
A relationship must exist between your entities for native filtering to work. For example, the Contact entity should have a Many-to-One relationship with Account through parentcustomerid.
If the relationship does not exist, create one by clicking New Relationship. Define the parent and child entities, then save and publish your changes. Missing relationships will cause filters to fail silently, so this verification step prevents troubleshooting later.
Step #2: Enable Related Records Filtering
The form editor contains built-in filtering options that appear once relationships are established. Navigate to the form where filtering should apply. Click on the lookup field you want to filter, such as Primary Contact on an Account form. The field properties panel opens on the right sidebar.
Locate the Related Records Filtering section within properties. Toggle on Only show records where to activate filtering.
A dropdown menu appears showing available relationships. Select the appropriate relationship from this list. The dropdown only shows valid connections based on the entities involved in your form, which prevents configuration errors.
Step #3: Configure the Filter Relationship
This step defines which field on your current form drives the filtering behavior. Specify the source field that controls the filter. For example, the Account lookup on an Opportunity form. The system will then filter the target lookup to show only related records.
Multi-hop filtering requires setting the relationship path carefully.
A scenario like Case > Account > Contact needs explicit mapping through each connection point.
Verify the field mappings align with your data model before proceeding. Complex relationship chains demand attention to ensure data flows correctly through each entity connection.
Step #4: Test and Publish
Configuration errors often surface during testing, so validation across multiple scenarios catches issues early. Save the form configuration and publish all customizations. Open an existing record to test the filtered lookup behavior.
Select a value in the parent field, then open the child lookup. Only related records should appear in the filtered list. Test with records that have no matching results to check empty state handling.
Clear the parent field and confirm the child lookup responds appropriately. If no records appear when you expect results, the relationship field may lack data on one or both entities.
2. Code-Based Filtering Using JavaScript

JavaScript filtering provides control over cascading lookup fields in Dynamics 365 when you need conditional logic, multiple filter criteria, or relationships that do not fit the native filtering model.
Step #1: Create the JavaScript Web Resource
Your filtering logic lives in a JavaScript file that gets stored as a web resource in Dynamics 365. Open your solution in Power Apps and navigate to Web Resources. Click New Resource and select JavaScript as the type. A descriptive name like filterLookups.js helps with future maintenance.
Write your function using the addPreSearch event pattern. Here is a template for filtering a City lookup based on Country selection:
function filterCityByCountry(executionContext) {
var formContext = executionContext.getFormContext();
var countryLookup = formContext.getAttribute("new_country").getValue();
if (countryLookup !== null) {
var countryId = countryLookup[0].id.replace(/[{}]/g, "");
var cityControl = formContext.getControl("new_city");
cityControl.addPreSearch(function() {
var fetchXml = "<filter type='and'>" +
"<condition attribute='new_countryid' operator='eq' value='" + countryId + "' />" +
"</filter>";
cityControl.addCustomFilter(fetchXml, "new_city");
});
}
}
Replace schema names with your actual field names. The addPreSearch function executes before the lookup query runs, injecting your filter criteria into the request.
Save and publish the web resource to make it available for forms.
Step #2: Register the Script on the Form
Forms need explicit references to web resources before they can execute the functions inside them.
Navigate to the target form and click Form Properties in the command bar. The properties dialog opens with several tabs visible.
Switch to the Form Libraries tab. Click Add Library and select your JavaScript web resource from the list. Loading order matters when scripts reference functions from other libraries, so position your filter script after any dependencies.
Multiple scripts on the same form require careful ordering to prevent reference errors.
Step #3: Add the OnLoad Event Handler
The form must call your function when it loads to initialize filtering behavior. Stay in Form Properties and switch to the Event Handlers tab. The Event dropdown should display OnLoad as an option.
Click Add to create a new event handler. Choose your web resource library from the Library dropdown. Enter your function name in the Function field, such as filterCityByCountry.
The Pass execution context as first parameter checkbox must be enabled—event handlers without execution context will fail because the function cannot access form data. Set the execution order if multiple OnLoad handlers exist on the same form.
Step #4: Configure the Parent Field OnChange Event
Filters must update dynamically when users change the parent field value. Click on the parent lookup field (Country in this example) directly on the form canvas. The properties panel opens on the right sidebar.
Navigate to the Events tab within field properties.
The OnChange event should appear in the dropdown. Click Add and choose your JavaScript library from the list. Enter the same function name used in the OnLoad event. Enable Pass execution context as first parameter here as well. This configuration ensures the City lookup refreshes its filter whenever the Country selection changes.
Step #5: Handle Edge Cases
Real-world usage includes scenarios where users clear fields or select values with no matching results.
Add logic to clear the filtered field when the parent becomes empty. The code formContext.getAttribute(“new_city”).setValue(null) resets the child field appropriately.
Place this code at the function start when countryLookup equals null.
Edge case handling prevents orphaned data where a City remains selected after removing its Country. Consider adding user notifications when filters return no results, so users understand why the lookup appears empty. Test behavior when switching between parent values rapidly to catch race conditions.
Advanced Techniques for Lookup Filtering
Basic filtering handles straightforward parent-child relationships, but complex business scenarios demand layered logic. Let’s take a closer look.
Cascading Lookups Across Multiple Fields
Multi-level cascading connects three or more lookups in sequence, where each selection filters the next. A geographic hierarchy demonstrates this pattern: Country > State > City. When a user selects the United States, the State lookup shows only U.S. states.
Selecting California then filters the City lookup to show only California cities.
Implementation requires chaining addPreSearch functions. The Country field triggers State filtering. The State field triggers City filtering. Each function checks that its parent field contains a value before applying filters:
function filterStateByCountry(executionContext) {
var formContext = executionContext.getFormContext();
var countryLookup = formContext.getAttribute("new_country").getValue();
if (countryLookup !== null) {
var countryId = countryLookup[0].id.replace(/[{}]/g, "");
var stateControl = formContext.getControl("new_state");
stateControl.addPreSearch(function() {
var fetchXml = "<filter type='and'>" +
"<condition attribute='new_countryid' operator='eq' value='" + countryId + "' />" +
"</filter>";
stateControl.addCustomFilter(fetchXml);
});
}
}
function filterCityByState(executionContext) {
var formContext = executionContext.getFormContext();
var stateLookup = formContext.getAttribute("new_state").getValue();
if (stateLookup !== null) {
var stateId = stateLookup[0].id.replace(/[{}]/g, "");
var cityControl = formContext.getControl("new_city");
cityControl.addPreSearch(function() {
var fetchXml = "<filter type='and'>" +
"<condition attribute='new_stateid' operator='eq' value='" + stateId + "' />" +
"</filter>";
cityControl.addCustomFilter(fetchXml);
});
} else {
formContext.getAttribute("new_city").setValue(null);
}
}
Critical to cascading logic: when users change a parent field, all dependent fields must clear. Changing Country should reset both State and City. Changing State should clear City but leave Country intact. Add setValue(null) calls in each function’s else block to handle these scenarios.
Register both functions on form load. Attach filterStateByCountry to the Country field’s OnChange event. Attach filterCityByState to the State field’s OnChange event. This creates a chain reaction where each selection propagates filters downward through the hierarchy.
Conditional Filtering Based on Record Type or User Role
Different users need different lookup options based on their role or the record they’re editing. A sales manager should see all accounts, while regional reps see only accounts in their territory. Premium support cases might allow linking to any knowledge article, while standard cases are restricted to public articles.
Role-based filtering uses Xrm.Utility.getGlobalContext().userSettings to retrieve the current user’s details:
function filterAccountsByUserTerritory(executionContext) {
var formContext = executionContext.getFormContext();
var userSettings = Xrm.Utility.getGlobalContext().userSettings;
var userId = userSettings.userId.replace(/[{}]/g, "");
// Retrieve user's territory (assumes custom field on User entity)
Xrm.WebApi.retrieveRecord("systemuser", userId, "?$select=_new_territoryid_value").then(
function success(user) {
if (user._new_territoryid_value) {
var accountControl = formContext.getControl("customerid");
accountControl.addPreSearch(function() {
var fetchXml = "<filter type='and'>" +
"<condition attribute='new_territoryid' operator='eq' value='" + user._new_territoryid_value + "' />" +
"</filter>";
accountControl.addCustomFilter(fetchXml);
});
}
},
function error(err) {
console.log("Error retrieving user territory: " + err.message);
}
);
}
Record type filtering examines form values to determine filter behavior. A Case form might filter knowledge articles differently based on case priority:
function filterArticlesByCasePriority(executionContext) {
var formContext = executionContext.getFormContext();
var priority = formContext.getAttribute("prioritycode").getValue();
var articleControl = formContext.getControl("new_relatedarticle");
articleControl.addPreSearch(function() {
var fetchXml = "<filter type='and'>";
if (priority === 1) { // High priority cases see all articles
fetchXml += "<condition attribute='statecode' operator='eq' value='0' />";
} else { // Standard cases see only public articles
fetchXml += "<condition attribute='statecode' operator='eq' value='0' />";
fetchXml += "<condition attribute='new_ispublic' operator='eq' value='1' />";
}
fetchXml += "</filter>";
articleControl.addCustomFilter(fetchXml);
});
}
Security roles can also drive conditional logic by checking userSettings.securityRoles:
This approach implements conditionally filter lookup fields without creating multiple forms or complex Dynamics 365 security models.
Common Issues and Troubleshooting Tips
Even correctly configured filter lookup fields in Dynamics 365 sometimes fail during deployment or break after platform updates.
| Issue | Why It Happens | Troubleshooting Tip |
| Filter not applying correctly | The relationship between the parent and child entities is missing or incorrectly defined. | Check entity relationships in Power Apps. Ensure the lookup field references a valid Many-to-One or One-to-Many relationship before enabling filtering. |
| Lookup shows all records instead of filtered ones | Related records filtering is turned off or not linked to the correct parent field. | In form properties, open the lookup field settings and confirm that “Only show records where” is toggled on and mapped to the correct field. |
| Filter works inconsistently | Cached form data or unrefreshed scripts can cause filters to behave unpredictably. | Clear browser cache, republish the form, and reload it in an incognito window to test fresh behavior. |
| JavaScript filter not triggering | The script is not properly linked in Form Properties or the event handler is missing. | Add the web resource under Form Libraries, then register the function for both OnLoad and OnChange events with “Pass execution context” enabled. |
| Filtered lookup returns empty results | Parent lookup has no value, or the filter query returns no matches. | Add logic to handle null or empty parent values in the JavaScript function. Optionally, display an alert to inform users when no related records exist. |
| Incorrect records appear in lookup | Field schema names used in code don’t match the actual CRM field names. | Double-check schema names in the table designer and update the JavaScript function accordingly before publishing changes. |
Streamline Your CRM With Aegis Softtech’s Expert Lookup Filtering
Filter lookup fields turn messy dropdown lists into precision tools. Your sales team stops scrolling through 5,000 contacts to find the three that matter. Dynamics 365 support agents see the right products instantly. Data entry becomes faster, cleaner, and way less frustrating.
The setup varies. Basic filters take ten minutes with point-and-click configuration. Complex cascading hierarchies or role-based rules need JavaScript expertise and real testing across different user types by Dynamics CRM consultants.
At Aegis Softtech, our Dynamics CRM Services team has built these systems for businesses across industries. Conditionally filter lookup fields that adapt to user roles, multi-entity chains that work under pressure, Dynamics 365 form scripting examples that handle edge cases gracefully—we handle the tricky parts so your forms just work.
Book a free consultation with our Dynamics 365 team, and we'll show you exactly how to fix them.


