Power Pages Is Not a Website Builder
Power Pages lets you expose Dataverse to external users without writing a custom web app. It's also the most misunderstood product in the Power Platform stack. Here's what it's actually for, and where it falls apart.
Someone on the project always asks for “a nice portal where customers can log in and see their stuff.” Power Pages can build that. It can also turn into a six-month fight with table permissions if you treat it like a website instead of what it actually is: a security-gated UI layer sitting directly on top of Dataverse.
What Power Pages Actually Is
Power Pages (the rebrand of Power Apps Portals) renders web pages that read and write Dataverse data for users who are not licensed Dataverse users — anonymous visitors, external customers, partners, citizens. Everything you build is ultimately a view into the same tables your model-driven app uses.
That’s the pitch and the trap in one sentence. It is not a general-purpose website framework. There’s no routing library, no arbitrary backend code, no npm ecosystem you can lean on for business logic. You get:
- Pages built from templates and web templates (Liquid + HTML)
- Forms and lists that bind directly to Dataverse views and forms
- Table permissions that decide, row by row, whether a given external user can see or touch a record
- Web roles that group external users and drive which permissions apply
If your mental model is “Next.js app with a Dataverse API,” you will fight the platform. If your mental model is “a configurable, permission-gated UI for Dataverse,” you’ll mostly just configure it.
The Three Things That Decide Whether This Project Goes Well
1. Table Permissions, Not App Logic
In a model-driven app, security roles gate access. In Power Pages, table permissions do — a separate, portal-specific layer that sits on top of (not instead of) your normal Dataverse security roles.
A table permission defines: which table, which scope (Global, Contact, Account, Parent, Self), and which operations (Create, Read, Write, Delete, Append, Append To). Scope is the part people get wrong. “Contact” scope means “records where a lookup field points back to the logged-in contact” — not “any Contact record.” If your table doesn’t have an obvious lookup back to the portal user, you need to add one, because table permission scoping can’t invent a relationship that doesn’t exist.
The failure mode is always the same: a developer builds the page, wires the list, tests it as a System Administrator in the back end, ships it — and the first real external user sees an empty list or a permission error. Test everything as a contact assigned to the actual web role you built, not as yourself.
2. Liquid, Not JavaScript, for Server-Side Logic
Pages use Liquid templating to pull Dataverse data server-side before the page renders:
{% raw %}
{% fetchxml case_query %}
<fetch>
<entity name="incident">
<filter>
<condition attribute="customerid" operator="eq" value="{{ user.contactid }}" />
</filter>
</entity>
</fetch>
{% endfetchxml %}
<ul>
{% for case in case_query.results.entities %}
<li>{{ case.title }} — {{ case.statuscode.label }}</li>
{% endfor %}
</ul>
{% endraw %}
This runs server-side, respects table permissions, and is the backbone of every real Power Pages site. Client-side JavaScript still exists for interactivity, but it talks to Dataverse through the Web API, which is also permission-gated — a clever script can’t bypass table permissions by calling the API directly. That’s a feature, not a bug people keep trying to work around.
The practical implication: most “can we just add this logic” requests are a Liquid snippet and a Fetch XML query, not a code project. If someone’s asking for conditional logic that Liquid genuinely can’t express — multi-step calculations, calling external services, anything stateful — that’s a sign the logic belongs in a Power Automate flow or a plugin, triggered from the portal action, not crammed into the page.
3. Authentication Is a Separate Decision From Authorization
Power Pages supports multiple identity providers — Microsoft Entra ID (External ID), local accounts, Azure AD B2C (legacy), or a mix. Picking the provider answers “who can log in.” Table permissions and web roles answer “what can they see once they’re in.” These get conflated constantly in planning meetings — “we’ll use Entra External ID for security” doesn’t mean anything about row-level access; you still build every table permission by hand.
Decide authentication early because it affects timeline — external identity provider setup, especially B2B scenarios with partner tenants, involves Azure configuration that isn’t a Power Platform maker’s job and needs an identity person in the room from week one.
What’s Actually Hard (and Isn’t About Power Pages)
Design system fights. Out-of-the-box Bootstrap styling looks dated immediately. Custom CSS/branding work is real front-end work — budget for it like a website project, not a “quick portal,” because that’s where most schedule overruns actually happen.
List performance at scale. Portal lists use the same FetchXML-backed paging as model-driven views. Thousands of rows with no indexed filter will be slow in exactly the way a poorly filtered model-driven view is slow — because under the hood, it is the same query engine.
Multi-table forms that mirror business process flows. Power Pages forms map cleanly to a single Dataverse form. A wizard-like, multi-step process that spans several tables needs multiple pages wired together with session state (Liquid has request.params and a few patterns for this), or a Power Automate-backed submission step. It’s doable, but it’s assembled from parts, not a single configuration screen.
Anonymous access. If any part of the site is public (no login), every table permission touched by that page needs careful scoping, because “anonymous” isn’t a web role you can lock down the usual way — it’s closer to “no identity at all,” and a misconfigured anonymous table permission is the fastest way to leak data to the entire internet.
When Power Pages Is the Wrong Tool
- You need full control over routing, SSR frameworks, or a design system that doesn’t bend to Liquid/Bootstrap conventions — build a real web app and call Dataverse through the Web API instead.
- The “external users” are actually just other employees without Dataverse licenses — check whether Team Member licensing or a different app makes more sense before building a portal to route around licensing.
- The logic is fundamentally transactional/computational (pricing engines, complex workflows) rather than CRUD-on-Dataverse — Power Pages will technically do it through Liquid and flows, but you’ll be fighting the templating language the whole way.
The Honest Scoping Question
Before quoting a Power Pages project, I ask one question: is this a UI for data that already lives in Dataverse, gated by row-level permissions? If yes, Power Pages is usually the fastest path — faster than a custom app, because table permissions and forms are configuration, not code.
If the real ask is “a marketing site with a login” or “a complex application with a few Dataverse lookups,” Power Pages will work, technically — and it’ll feel like building furniture with the wrong tools the entire time. Scope it as what it is: a secured window into Dataverse, not a website.
Related articles
Managed vs Unmanaged Solutions, Explained Properly
Every Dataverse project hits this question eventually: managed or unmanaged? Here's what the two actually mean, why the choice trips people up, and a rule of thumb that holds in practice.
Should You Build a PCF Control?
Power Apps Component Framework lets you build React-based custom controls for model-driven forms and views. Most of the time, you shouldn't. Here's when PCF is the right answer — and what the tooling docs don't warn you about.
Power Platform Licensing: What I Wish Someone Told Me on Day One
Licensing isn't a procurement problem — it's an architecture decision. Here's the mental model every solution architect needs before starting a Power Platform project, with real scenarios and a pre-project checklist.