June 12, 2025

Embed PDFs in Power Apps Using the Experimental PDF Viewer

Introduction

Power Apps now offers an experimental PDF Viewer control that lets you display PDFs directly within your app interface. This is especially helpful in use cases like displaying reference documents, training guides, or reports stored in SharePoint Document Libraries - without requiring users to download them.

Below is a detailed, step-by-step guide to help you display a PDF stored in SharePoint within your Power Apps application using the experimental PDF Viewer control. 

Power Apps - PDF Viewer

Step-by-Step Instructions:

Step 1: Open or Create a Power Apps Project 

  • Navigate to https://make.powerapps.com.
  • Either:
  • Create a new Canvas app (start from blank), or
  • Open an existing app where you want to display the PDF.

Step 2: Connect to SharePoint Document Library 

  1. From the left panel, click on Data.
  2. Click on + Add data.
  3. Search for SharePoint and select it.
    Add data source in Power apps

  4. Choose your account, paste or search for your SharePoint Site URL, and then click Connect.
  5. Select your Document Library where your PDFs are stored, then click Connect.
  6. (Optional) Open your SharePoint Document Library in a new tab and upload your PDF if it's not already available. This ensures the document can be linked and previewed in Power Apps.

Step 3: Insert the Experimental PDF Viewer 

  1. In Power Apps, click on the Insert or + icon on the left toolbar.
  2. Use the search bar to search for PDF Viewer (experimental).
    Insert PDF viewer (experimental)

  3. Click to insert it into your screen.

Step 4: Set Up PDF Viewer to Display a File 

Start by using this formula in the Document property of the PDF Viewer: 

LookUp(Documents, Name = "Sample PDF.pdf").'Link to item'

Note: This may not always work as expected in Power Apps. If the PDF doesn't load, try the workaround method below to generate a direct file link. 

Workaround: Generate a Direct PDF URL Manually 

Sometimes Power Apps doesn't support rendering the default SharePoint link ('Link to item'). Instead, you can manually create a direct PDF link. 


Steps to generate a working PDF link: 

1. In SharePoint Document Library:

  1. Click on + Add Column > Select Hyperlink.
  2. Name it: Link

2. Once the column is created, go to Library Settings, then click on the Link column. 

  1. Change Format URL as: from Hyperlink to Picture.
    Library Setting - Hyperlink to Link

3. Back in your Document Library: 

  1. Click on the ellipsis (...) next to the PDF file.
  2. Click Open in browser. The PDF opens in a new tab.
  3. Copy the full URL. Remove everything after .pdf so that the link ends with .pdf.

4. Go back to your document library and:

  1. Click on Edit in grid view.
  2. Paste this cleaned-up PDF URL into the Link column for that file.
  3. Click Exit grid view to save changes. 

Edit in grid and update the url - remove extra from the link ends with .pdf
Step 5: Update Power Apps to Use the Cleaned URL 

Finally, update the Document property of the PDF Viewer to reference your new link column: 

LookUp(Documents, Name = "Sample PDF.pdf").Link

Success! Your PDF should now render properly inside the Power Apps screen. 

Show PDF in Power Apps using PDF viewer


Pro Tips:

  • Use Preview mode (F5) to test PDF rendering.
  • Ensure your PDF link ends with .pdf - any additional query parameters or tokens may break it.
  • This experimental control works best with direct-access or publicly accessible links.

Use Case Example:

This method is ideal for onboarding portals, document approval systems, training guides, or resource libraries where documents need to be viewed quickly without download. 


Conclusion:

Embedding PDFs within Power Apps is now easier thanks to the experimental PDF Viewer control. With a direct SharePoint link configured properly, you can enable seamless in-app PDF viewing for onboarding guides, reference documents, and more - without needing third-party tools or downloads. 

If you have any questions you can reach out our SharePoint Consulting team here.

Best Practices for SharePoint Term Store and Power Automate Integration

Introduction:

Managing metadata effectively is crucial for scalable and maintainable SharePoint environments. SharePoint’s Term Store provides a centralized way to manage taxonomy, while Power Automate enables automation across your organisation. When combined, these tools can significantly enhance content tagging, data consistency, and process automation. In this post, we’ll walk through best practices for integrating the SharePoint Term Store with Power Automate.

1. Understand the Term Store Structure

Before integrating, ensure you understand how the Term Store is organised:

  • Term Groups: Top-level containers for organising Term Sets.
  • Term Sets: Collections of related terms.
  • Terms: Individual metadata entries used for tagging.

Ensure proper governance and naming conventions are in place to prevent confusion or duplication.


2. Use PnP PowerShell or PnPjs for Bulk Term Management

Managing terms programmatically (via PnP PowerShell or PnPjs in SharePoint Framework) is more efficient than manual entry, especially in large taxonomies.

  • Automate term provisioning.
  • Keep staging and production environments in sync.

Example: Use Power Automate to trigger a PowerShell script that syncs term sets from a central source.

3. Accessing the Term Store in Power Automate

Out-of-the-box Power Automate connectors do not provide native actions to query the Term Store. Workarounds include:

  • HTTP Requests to SharePoint REST API
  • Azure Functions or Custom Connectors
Example REST API call:

GET _api/v2.1/termStore/sets('{termSetId}')/terms

Ensure the flow has the correct permissions (App-Only or Delegated permissions via Azure AD).

4. Create Reusable Flows for Term Retrieval

Develop modular flows to fetch terms from a term set and reuse them across different workflows:

  • Input: Term Set ID
  • Output: List of terms (as an array or JSON)

This promotes reusability and reduces redundancy.

5. Use Term GUIDs, Not Labels

Avoid using plain text labels when referencing terms in automated flows.

  • Use the term GUIDs to ensure uniqueness
  • Prevent issues with label duplication or localisation
6. Dynamic Tagging in SharePoint List Items

Use retrieved term GUIDs to tag list items dynamically:


"TaxCatchAll": [
  {
    "Label": "India",
    "TermID": "b8b3a6ab-0c4d-4c4a-8a9e-d0e74f9623fe"
  }
]

Use the Send an HTTP request to SharePoint action to update list items programmatically.

7. Error Handling and Logging

Include error handling for API calls:

  • Retry policies
  • Logging to a SharePoint list or Dataverse table
  • Email alerts for failures
8. Security and Permissions
  • Ensure flows run under an account with Term Store access
  • Use Azure-managed identities or certificate-based app registration for sensitive operations
9. Limit API Calls and Throttle Efficiently

SharePoint API has throttling limits:

  • Use pagination when querying large term sets
  • Implement delays using Delay action in Power Automate
10. Maintain Documentation

Document:

  • Term Set structures
  • API endpoints
  • Power Automate flow logic

This helps in onboarding, debugging, and long-term maintenance.


Conclusion:

When integrated correctly, the SharePoint Term Store and Power Automate provide a powerful solution for automating metadata-driven processes. By following these best practices, you ensure scalability, reliability, and maintainability across your organisation. Start small, build reusable components, and always document your integrations.

If you have any questions you can reach out our SharePoint Consulting team here.

Performance Optimization in React: Real-World Examples

Introduction

React is powerful, but it can suffer performance issues when components re-render unnecessarily or handle more work than needed.

This guide explores 6 common performance bottlenecks in real-world applications — along with practical techniques to address them. Each example includes before-and-after code and clear explanations of what’s happening, why it matters, and how to optimize it effectively.


1. Unnecessary Re-renders in Child Components

Consider a simple Counter component that displays a number. Even if only the input field is updated, the Counter still re-renders - a common inefficiency in many apps.

Before Optimization

function Counter({ count }) {
  console.log("Counter rendered");
  return <h2>Count: {count}</h2>;
}

function App() {
  const [count, setCount] = React.useState(0);
  const [text, setText] = React.useState("");

  return (
    <div>
      <Counter count={count} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
}

After Optimization (using react-window)

const Counter = React.memo(function Counter({ count }) {
  console.log('Counter rendered');
  return <h2>Count: {count}</h2>;
});

What's happening?
Every time the App component re-renders - even when updating the text input — the Counter component also re-renders.
Why it's a problem?
React re-renders all child components by default, even if their props haven’t changed. This adds up fast in large trees.
Optimization Approach
Use React.memo to prevent unnecessary re-renders by memoizing the component unless its props actually change.

2. Rendering Huge Lists Without Virtualization

Rendering thousands of DOM nodes can completely freeze your browser. If you ever render a long list — this is critical.

Before Optimization

function App() {
  const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);

  return (
    <div style={{ height: '400px', overflowY: 'auto' }}>
      {items.map((item) => (
        <div key={item}>{item}</div>
      ))}
    </div>
  );
}

After Optimization (using react-window)

import { FixedSizeList as List } from 'react-window';

function App() {
  const items = Array.from({ length: 10000 }, (_, i) => `Item ${i}`);

  return (
    <List
      height={400}
      itemCount={items.length}
      itemSize={35}
      width={'100%'}
    >
      {({ index, style }) => (
        <div style={style}>{items[index]}</div>
      )}
    </List>
  );
}

What's happening?
All 10,000 list items are rendered and pushed into the DOM at once.

Why it's a problem?
Rendering thousands of DOM nodes is memory-intensive and causes the browser to lag or freeze.

How to fix it?
Use virtualization (react-window) to render only visible items and reuse DOM nodes during scroll. Smooth performance, minimal overhead.


3. Function Re-created on Every Render

Every render creates a new function instance (e.g., the onClick handler in a button). This can cause unnecessary re-renders of child components, especially when they are memorized.

Before Optimization

function App() {
  const [count, setCount] = React.useState(0);
  return <Button onClick={() => setCount(count + 1)} />;
}

After Optimization

const handleClick = React.useCallback(() => {
  setCount((prev) => prev + 1);
}, []);
return <Button onClick={handleClick} />;

What's happening?
The inline arrow function gets recreated every time the component re-renders, so it has a new reference each time.

Why it's a problem?
If the Button component is memoized, it will still re-render because the onClick prop changes by reference.

How to fix it?
Wrap the function in useCallback to keep the reference stable across renders and avoid unnecessary updates downstream.

4. Tab Content Re-renders on Every Switch

Switching between tabs causes all tab content to unmount and remount, losing state and triggering unnecessary re-renders.

Before Optimization

{activeTab === 'profile' && <Profile />}
{activeTab === 'settings' && <Settings />}

After Optimization

const tabs = useMemo(() => ({
  profile: <Profile />,
  settings: <Settings />,
}), []);
return tabs[activeTab];


What's happening?
Each tab component mounts/unmounts when switching, resetting its internal state and triggering re-renders.

Why it's a problem?
Components lose their internal state, and there's a noticeable delay on tab switches due to remounting.

How to fix it?
Store components in `useMemo` and toggle visibility instead of remounting. This keeps the component’s state and makes switching faster.


5. Uncontrolled Component Switching to Controlled

Switching an input from uncontrolled to controlled (e.g., setting its value from undefined to a real value) leads to a React warning and a forced re-render.

Before Optimization

function Input({ value }) {
  return <input value={value} onChange={() => {}} />;
}

When value is initially undefined, this becomes uncontrolled, and when it gets a value, React throws a warning and forces a re-render

After Optimization

function Input({ value }) {
  return <input value={value ?? ''} onChange={() => {}} />;
}

What's happening?
If value starts as undefined, React treats the input as uncontrolled. When it gets a value later, it switches to controlled — and React throws a warning.
Why it's a problem?
Switching from an uncontrolled to a controlled input in React causes the field to reset and displays a warning.
How to fix it?
Use a fallback value (value ?? '') to keep the input controlled from the beginning, avoiding warning and preserving expected behavior.


6. Large Components Doing Too Much

Rendering too many components at once in a large bundle can delay the first paint, making your app feel slow and unresponsive.

Before Optimization

function Dashboard() {
  return (
    <>
      <HeavyChart />
      <LiveFeed />
      <WeatherWidget />
      <NotificationPanel />
    </>
  );
}

All components are rendered and mounted at once.

After Optimization (Code Splitting)

const HeavyChart = React.lazy(() => import('./HeavyChart'));

function Dashboard() {
  return (
    <Suspense fallback={<Loader />}>
      <HeavyChart />
      {/* Other components can load similarly */}
    </Suspense>
  );
}

What's happening?
All dashboard components are loaded upfront, even if they’re not visible or immediately needed.

Why it's a problem?
It bloats the initial JavaScript bundle, slowing down your app's first render and hurting metrics like LCP.

How to fix it?
Use React.lazy with Suspense to load components only when you need them. This helps the app load faster and feel more responsive.


Final Thoughts

Performance issues in React often creep in silently - small inefficiencies that add up as your app grows. These optimization patterns are widely applicable and can help improve performance and user experience across React applications of all sizes.