Custom JavaScript and Liquid in Power Pages Without Breaking on Refresh

A technical guide for web developers whose Power Pages code vanishes or breaks: where code belongs, which places survive design studio edits, one shared bundle, readable Liquid, secure flow calls and source control with the Power Platform CLI.

If your Power Pages JavaScript disappears after a refresh, it usually lives in page copy HTML, which the studio rewrites, or it runs before a list or form has rendered. Put code where it survives: each page's custom JavaScript and CSS, web files served as static assets, one Liquid include or head snippet that loads them, and content snippets for text only. For many pages, load one shared bundle from a web file and initialise per page from a data attribute. Keep Liquid in small named web templates. Call a Power Automate flow or Dataverse logic on submit through the Power Pages flow trigger or the Web API with table permissions, never with secrets in the browser.

Why does custom JavaScript disappear or stop working in Power Pages?

There are four common causes, and only one of them is the code actually being deleted.

The first is code pasted as a script tag into the page copy through the design studio text editor or a copy of the HTML. The studio treats page copy as content it owns, and a later visual edit can reformat or strip markup it does not expect. The second is timing: lists and some form elements render after the page loads, so a script that runs on page load finds nothing to attach to and appears to work only on some refreshes. Multistep forms also post back and redraw the page, so handlers attached once are gone on the next step.

The third is editing the wrong record. On sites with language support, a page has a root record and a localised content page, and the site renders the content page. Script typed into the root page in the Portal Management app does not appear, and script typed into one language does not appear in another. The fourth is caching: web files and page content are cached by the site and the browser, so a change can look lost until the cache refreshes. Check current Microsoft documentation for how to clear the site cache in your version.

Where can custom code legitimately live in Power Pages, and which places survive design studio edits?

Every place below is supported. The difference is who else edits it and what the studio regenerates. Put behaviour in places developers own, and keep content editors in places that hold content.

LocationGood forSurvives design studio edits?Watch out for
Script tags inside page copy HTMLNothing lastingNot reliably, the studio owns this markupVisual edits reformatting or removing the script
Page custom JavaScript and custom CSS (Edit code in the studio)Code specific to one pageYes, stored separately from page copyEdit the localised content page, not only the root page
Web files (for example /js/site.js)Shared bundles, libraries and stylesheetsYes, the studio does not rewrite your own web filesDo not edit theme.css, which the styling workspace regenerates, and allow for caching after updates
Header or layout web template with one Liquid includeLoading shared files on every pageGenerally yes, but recheck after changing header settings in the studioKeep it to one line so a template change cannot lose logic
Head content snippet such as Tracking Code, where your template renders itScript and style references in the page headYes, but editors can change snippetsBusiness users editing it by accident
Content snippetsReusable text and small markupYesNot a place for behaviour
Basic form and multistep form custom JavaScriptValidation and behaviour for one formYesRuns again after each postback

How should shared JavaScript be structured across dozens of pages?

One bundle, loaded once, doing nothing until a page asks for it. Copying the same fifty lines into the custom JavaScript of thirty pages is how a fix gets applied to twenty-eight of them.

Build the site script as a single file, bundled and minified if you use a build step, and upload it as a web file such as /js/site.js with a version in the reference so browsers fetch the new copy. Load it once from the layout. In your page templates, write a data attribute on a wrapping element, for example data-page="case-detail", using a value set in the web template or read from a site marker. The bundle then looks up a small registry of initialisers by that value and runs only the matching one.

That leaves each page's own custom JavaScript either empty or a single call with page-specific settings. What the file weighs and how it loads is covered in our Power Pages performance guide; defer the script and keep libraries out of the bundle unless most pages use them.

  • Wrap everything in a namespace such as window.Site, and never rely on global variables from other pages.
  • Keep one initialiser per page type, each checking that its elements exist before touching them.
  • Wait for lists to finish rendering by handling the list's loaded event rather than page load, and reattach form handlers after every multistep form postback.
  • Delegate events to a stable parent element so re-rendered rows keep their behaviour.
  • Log errors to the console with the page type, and send them to Application Insights if you use it, so a broken initialiser is visible instead of silent.

How do you organise Liquid so it stays readable across many templates?

Treat web templates as components, not as pages. The templates people cannot read are the ones where a single page template holds layout, queries, loops and markup for every variation.

  • Use one layout web template with blocks, and have page templates extend it and fill named blocks rather than copying the header and footer.
  • Move repeated markup into small partial templates and pull them in with include, passing parameters explicitly instead of relying on variables set elsewhere.
  • Keep each fetchxml query in one template near where its results are used, name the result variable after what it holds, and never run a query inside a loop.
  • Prefix template names by purpose, such as Layout, Partial and Data, so the list in the Portal Management app and the downloaded folder sorts into something navigable.
  • Keep business rules out of Liquid where Dataverse can enforce them, because Liquid only shapes what one page shows.
  • Comment the intent at the top of each template with a Liquid comment block, which is not sent to the browser.
  • Use content snippets for text editors change, so a wording change never needs a template edit.

How do you validate a basic or multistep form before it submits?

Use the hooks the forms already provide rather than intercepting the submit button. Basic forms call a client validation function before they post, and multistep forms have an equivalent for each step. Your form custom JavaScript wraps the original function, runs its own checks, shows a message in the validation summary and returns false to stop the submit.

Client validation is for the user's convenience only. Anyone can bypass it with the browser tools, so every rule that matters must also hold on the server: required columns and business rules on the Dataverse table, a plug-in for anything more complex, and table permissions that limit which records the user can create or change. The permission side is covered in depth in our guide to fixing Power Pages table permissions.

How do you call a Power Automate flow or custom logic securely when a form is submitted?

Never put a flow URL with its signature, an API key or a client secret in JavaScript, a web file or a snippet. Anything the browser can read, any visitor can read and reuse. Use one of the routes where the site authenticates the call and the secret stays on the server.

RouteHow it worksSecurity points
Power Pages cloud flow integrationAdd a flow with the Power Pages trigger to the site, assign web roles, and call it from JavaScript through the site cloud flow endpoint with the anti-forgery tokenOnly users in the assigned web roles can call it; validate every input in the flow; do not trust a record or contact identifier sent from the browser without checking it
Portal Web API plus server-side logicJavaScript creates or updates a Dataverse row through the site Web API; a plug-in or a Dataverse-triggered flow does the restWeb API must be enabled per table and fields in site settings, and table permissions decide what the user can write
Standard form plus server-side logicThe basic or multistep form saves the row, and a plug-in or flow on create does the processingNo custom endpoint at all; often the simplest and safest choice
  • Keep credentials for external systems in the flow connection, Dataverse environment variables backed by Azure Key Vault, or the plug-in configuration, never in the site.
  • Include the anti-forgery token on every Web API and flow call, using the token helper shown in current Microsoft documentation.
  • Return only what the page needs to show, not the full response from the external system.
  • Check current Microsoft documentation for what the Power Pages flow trigger passes about the signed-in user and for any limits on flow calls.

How do you put Power Pages code under source control with pac pages download and upload?

Download the site to a folder, commit it, and make changes through that folder rather than in several browser tabs. The Power Platform CLI command pac pages download (formerly pac paportal download) writes the site configuration as files: web templates, content snippets, site settings, web files, and each page's copy, custom JavaScript and custom CSS as separate files beside its YAML. pac pages upload writes changes back, and the --modelVersion option tells the CLI whether the site uses the standard or enhanced data model.

That makes every JavaScript and Liquid change a reviewed commit with a diff, instead of an edit nobody can trace. Build your bundle in the repository and copy the output into the web file's folder before upload. How the CLI, the repository and a pipeline fit together for solutions is on our Power Platform ALM page, and moving a whole site between development, test and production, including deployment profiles and the records that are usually missed, is covered in deploying Power Pages between environments.

  • Agree one rule: code is changed in the repository and uploaded, and studio edits are limited to content and layout.
  • Download before you start work, because someone may have edited content in the studio since your last upload.
  • Never commit secrets, and exclude local build folders from the repository.

How do you stop design studio edits and code changes overwriting each other?

Separate the owners. Most overwrites happen because a developer uploads a folder downloaded last week, replacing a page a content editor changed yesterday, or an editor pastes a fresh copy of a page over script that was in the page copy.

Keep all behaviour in custom JavaScript, form scripts and web files, where editors do not work. Download immediately before upload and review the diff for content changes you did not make. After editing code with Edit code in the studio, use Sync in the design studio so the open session reflects it. And check changes in a development site first, then move them forward, rather than editing production in the studio and trying to reconcile the repository afterwards.

How do you debug Power Pages JavaScript that only fails for some users?

Reproduce as the user, not as an administrator. Scripts often fail only for a signed-in contact with a particular web role, because the list, form or column they expect is hidden by table permissions or the page is not visible to that role.

Sign in to a development site with a test contact in the same web roles, open the browser developer tools, and check the console for errors, the network tab for failed Web API or flow calls and their status codes, and the elements tab for whether the list or form markup exists at all. A Web API call that returns a permissions error is telling you about table permissions or site settings, not about your JavaScript. Check that the web file loaded the version you expect, and clear the cache if it did not.

Should a portal with this much custom code stay on Power Pages?

Usually yes. Custom JavaScript, web files and Liquid are normal Power Pages development, and a well-structured bundle keeps a portal maintainable for years. What a full Power Pages build covers is on our Power Platform consulting page.

The question worth asking is how much of the site is now custom front end with Power Pages underneath. We recommend the right solution - whether that's Microsoft Dynamics 365, Power Platform, or a custom-built CRM. Some businesses need the Microsoft ecosystem. Others need full control without licensing. We deliver both. If most screens have become bespoke JavaScript, a React front end over the Dataverse Web API or a custom-built CRM on React, Node.js, PostgreSQL or .NET may be simpler to own, and our Power Pages or custom front end comparison sets out when that switch is justified.

How does Solzet help structure custom JavaScript and Liquid in Power Pages?

We start by downloading the site into a repository and mapping where code currently lives: page copy, custom JavaScript fields, snippets, web files and forms. Then we move behaviour into one shared bundle with page-scoped initialisers, split large templates into readable partials, move secrets and external calls behind the flow trigger or server-side logic, and set up upload through the CLI so every change is reviewed. The portal keeps working throughout, because each page moves on its own.

The work is done by senior consultants and full-stack developers delivering remotely from Yerevan, Armenia, with 8+ years of Dynamics 365 Customer Engagement and Power Platform work, directly for your team or white-label for Microsoft partners. Security questions go through our table permissions guide, and slow pages through our Power Pages performance guide.

What do people ask us?

Why does my Power Pages custom JavaScript disappear after a refresh?

Usually because it sits as a script tag in page copy HTML, which the design studio can rewrite on the next visual edit, or because it runs before a list or form has rendered, so it only seems to work sometimes. It can also be typed into the root page instead of the localised content page the site renders, or hidden by caching. Move it to the page custom JavaScript or a web file.

Where should I put JavaScript that runs on every Power Pages page?

In a web file such as /js/site.js, referenced once from the layout web template or a head snippet your template renders. Add a version to the reference so browsers fetch updates, and do not add it to theme.css or other files the styling workspace regenerates. Keep page-specific behaviour in initialisers selected by a data attribute on the page.

Does Power Pages design studio overwrite custom code?

It can overwrite markup inside page copy, because the studio owns that content, and the styling workspace regenerates its theme file. Page custom JavaScript and CSS, your own web files, web templates and form custom JavaScript are stored separately and are not rewritten by ordinary visual edits. After editing code, use Sync in the studio so an open session does not save stale content.

How do I run JavaScript after a Power Pages list has loaded?

Attach your code to the list's loaded event rather than page load, because lists fetch and render their rows after the page arrives, and again when users page, sort or search. Delegate click handlers to a stable parent element so they keep working on re-rendered rows. For multistep forms, reattach handlers after each step, since the page is redrawn on postback.

How do I call a Power Automate flow securely from a Power Pages form?

Use the Power Pages cloud flow integration: add a flow with the Power Pages trigger to the site, assign the web roles allowed to run it, and call it from JavaScript with the anti-forgery token. Validate every input inside the flow. Never place a flow URL with its signature, an API key or a secret in client JavaScript, because any visitor can read it.

Can Power Pages JavaScript call the Dataverse Web API?

Yes, through the portal Web API, once it is enabled for the table and its fields in site settings. Every call runs as the signed-in contact and is limited by table permissions, so the permissions are the security boundary rather than the script. Include the anti-forgery token and request only the columns you need. Check current Microsoft documentation for the supported operations.

How do I source control Power Pages JavaScript and Liquid?

Download the site with pac pages download, commit the folder, make changes there and write them back with pac pages upload, using --modelVersion for the standard or enhanced data model. Web templates, snippets, web files and each page's custom JavaScript and CSS appear as files, so every change gets a reviewable diff. Download again immediately before uploading.

How should Liquid be organised across many Power Pages templates?

Use one layout template with blocks that page templates extend, small partials pulled in with include and explicit parameters, each fetchxml query kept near where its results are used and never inside a loop, and consistent name prefixes. Put editable text in content snippets and keep rules Dataverse can enforce out of Liquid.

Which solution is right for your business?

Tell us what you need. A senior consultant replies within one business day with a recommendation - Dynamics 365, Power Platform, or a custom-built CRM - not a sales script.