Create an image/video gallery using Angular and Photoswipe


In this tutorial I will cover how you can create an art gallery for your website using Angular and Photoswipe. Angular is used for the presentation logic, we will handle a list of items as a scope-variable and an Angular controller for fetching the list, perhaps cleaning the data and so on. Photoswipe is the library used in this tutorial the “gallery functionality”, which includes displaying an image in fullscreen, swiping left and right and so on. The reason why I chose Photoswipe is because it is easy to use, very customizable and also works very well on mobile devices. You can read more about Photoswipe here.

To see a demo of how a gallery like this can look, please view my art gallery that I build using these technologies.

Displaying videos

When I first started working on this I had an idea that I also wanted to be able to show videos in the gallery, not just images. If an item in the gallery was a video a play-icon should be displayed over the thumbnail to indicate it is a video, and if opened the video should automatically start playing and be stopped when closing or swiping to next item. Unfortunately I found out that Photoswipe has no video support, it is just for images. However I managed to find a solution to this that I am happy with. In this tutorial I will cover what I did.

Setup Photoswipe

First of all, a default Photoswipe template must be added to your site. This template contains the markup used for the gallery interface. It should just be copypasgted, do not change classes and id’s in this template as it will mess up Photoswipe.

Here is how your index-file could look (in this case I am using php for the include function):

<!DOCTYPE html>
<html xmlns="http:/" lang="en">
        <link rel="stylesheet" href="css/myCss.css">
        <title>My cool gallery</title>
    <body ng-app="myApp" ng-controller="galleryController">
        <?php include("photoswipe.php"); ?>

This is the contents of the file photoswipe.php:

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
    <!-- Background of PhotoSwipe.          It's a separate element as animating opacity is faster than rgba(). -->
<div class="pswp__bg"></div>
<!-- Slides wrapper with overflow:hidden. -->
<div class="pswp__scroll-wrap">
        <!-- Container that holds slides.             PhotoSwipe keeps only 3 of them in the DOM to save memory.             Don't modify these 3 pswp__item elements, data is added later on. -->
<div class="pswp__container">
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<div class="pswp__item"></div>
<!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
<div class="pswp__ui pswp__ui--hidden">
<div class="pswp__top-bar">
                <!--  Controls are self-explanatory. Order can be changed. -->
<div class="pswp__counter"></div>
<button class="pswp__button pswp__button--close" title="Close (Esc)"></button>
                <button class="pswp__button pswp__button--share" title="Share"></button>
                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>
                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
                <!-- Preloader demo -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
<div class="pswp__preloader">
<div class="pswp__preloader__icn">
<div class="pswp__preloader__cut">
<div class="pswp__preloader__donut"></div>
<div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
<div class="pswp__share-tooltip"></div>
<button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
<div class="pswp__caption">
<div class="pswp__caption__center"></div>


Like I said, this is Photoswipes default template markup, I have not created this.

Now, as you can read in the Photoswipe getting started tutorial you should add references to the files photoswipe.css, default-skin.css, photoswipe.js and photoswipe-ui-default.min.js.

<!DOCTYPE html>
<html xmlns="http:/" lang="en">
        <link rel="stylesheet" href="css/myCss.css">
        <link rel="stylesheet" href="css/photoswipe.css">
        <link rel="stylesheet" href="css/default-skin.css">
        <script type="text/javascript" src="js/photoswipe.js"></script>
        <script type="text/javascript" src="js/photoswipe-ui-default.min.js"></script>
        <title>My cool gallery</title>
    <body ng-app="myApp" ng-controller="galleryController">
        <?php include("photoswipe.php"); ?>

Photoswipe should now be setup and ready to use!

The controller

Time to get going! I will start by working on the Angular controller and the corresponding view. Here’s how the controller that we will use will look like:

(function() {
    var app = angular.module('myApp');

    var galleryController = ['$scope', function($scope) {

        // Public variables
        $scope.loading = true; // Set loading to false when initialization is done
        // The list of items to use
        $scope.list = [{
            thumbnail: 'thumbnail_for_item_1.png',
            bigImage: 'image_1.png',
            width: 640,
            height: 640,
            index: 0
        }, {
            thumbnail: 'thumbnail_for_item_2.png',
            bigImage: 'image_2.png',
            width: 640,
            height: 640,
            index: 1
        }, {
            thumbnail: 'thumbnail_for_image_1.png',
            video: 'video.mp4',
            width: 640,
            height: 640,
            index: 2
        // Private variables
        // The list that Photoswipe will use
        var itemsForPhotoswipe = [];

        // Public methods
        $scope.openPhotoSwipe = openPhotoSwipe;

        function openPhotoSwipe(artItem) {
            var pswpElement = document.querySelectorAll('.pswp')[0];

            // define options (if needed)
            var options = {
                // history & focus options are disabled on CodePen
                history: false,
                focus: false,
                index: artItem.index,

                showAnimationDuration: 0,
                hideAnimationDuration: 0

            var gallery = new PhotoSwipe(pswpElement, 

        function init() {
            for (var j = 0; j < $scope.list.length; j++) {
                if ($scope.list[j].bigImage) {
                        src: $scope.list[j].bigImage,
                        w: $scope.list[j].width,
                        h: $scope.list[j].height,
                        index: $scope.list[j].index
                } else if ($scope.list[j].video) {
                        html: '<div class="videoSlide">' +
                              '<div class="videoSlideInner">' +
                              '<video width="100%" height="100%" id="videoPlayer' +
                              $scope.list[j].index + '" controls><source src="' +
                              $scope.list[j].video.url + '" type="video/mp4" />' +
                              '</video>' +
                              '</div>' +
                        index: $scope.list[j].index

            $scope.loading = false;


    app.controller('galleryController', galleryController);

As you can see in this example I have a hardcoded list called “list” that contains all the items to be displayed. You could also choose to load a list from a JSON-file or something if you like. As you can see items can either have a “bigImage” property if it’s an image or a “video” property if it’s a video.

In the init-function I loop through this list and create the list, itemsForPhotoswipe, that Photoswipe will use. This is because Photoswipe want’s the input list to have a specific format for each item. Properties like src, w, h, index and html are what Photoswipe expects.

The html-property allows the user to have a custom html template for displaying the current item. As you can see in the controller above I am using this to have a customt template for video items. With this a HTML5 video element will be shown for videos. Here is the CSS I used for the videoSlide container:

.videoSlide {
    width: 100%;
    height: 100%;
    text-align: center;
    display: table;
.videoSlide .videoSlideInner {
    height: 100%;
    display: table-cell;
    vertical-align: middle;
.videoSlide .videoSlideInner video {
    max-width: 100%;

This will center the video vertically and horizontally and make sure it never stretches outside the screen. Add this to your css-file.

The function openPhotoSwipe is the function to be called when clicking on a thumbnail in the gallery. The function takes the entire item object as inparamter and then initializes Photoswipe.

The view

Now it’s time for the view. It should look a little bit like this:

<div ng-show="loading">
<div data-ng-show="!loading">
    <img class="galleryImg"
    src="{{ ? 'img/play_icon.png' : '' }}"
    style="background-image: url({{item.thumbnail}});"
    data-ng-repeat="item in list"/>

This will generate a wall of thumbnail tiles for items in our list scope-variable. I use an img-element for this because I wan’t to be able to set a src-attribute for a play icon overlaying the thumbnail for video items. The video items will still have the thumbnail beneath the play icon. To see an example of this just view my gallery.

Almost done, minor tweaks next

Like I mentioned before, Photoswipe doesn’t really support videos right off the bat. We have made sure that video items now will we displayed with our custom template but we need to add some presentation logic for playing/pausing the video.

To do this I modified the file photoswipe.js as it was the simplest way to solve this. You can download my modified file from my Github repository here:

To see exactly what I did to the file you can analyze this commit:

The functions stopVideo and startVideo handles playback and pausing. As you can see identifiers such as videoPlayer + index is used to find the specific video element by matching with the current items index value. That is why I set id of the video element to videoPlayer + $scope.list[j].index in the controller when looping through the list.

With these additional changes Photoswipe will now automatically start a video when clicking on a video item, stopping when closing the item or swiping to the next.

Thank you for reading this tutorial, I hope you liked it 🙂


4 thoughts on “Create an image/video gallery using Angular and Photoswipe

  1. Debo

    Thanks for the tutorial. It solves my one issue for self hosted video. But what about Youtube? Have you tried that? In your another gallery you have used lightgallery for YouTube video.
    It would be great if u try for YouTube and share your work.


    1. Hello!
      I guess the gallery you are talking about is my video gallery found on this page:

      I actually used another plugin for that gallery, one called “Light gallery” which is a jQuery plugin. You can find it here:

      Here is a demo where YouTube videos are used:

      I know it’s not ideal to have to use two different plugins for different galleries. I made my art gallery using Photoswipe first, and used this fix as explained in the blog post to be able to display self hosted videos in that gallery. Much later I wanted to have a video gallery of Youtube videos but wasn’t able to use Photoswipe for that, and figured it would be too much work to tweak it further so I simply googled a bit and found out about Light gallery.

      I suggest simply looking into Light gallery instead. It seems to be able to do pretty much everything that Photoswipe does but with built in video support, which is better because ideally you don’t want to have to “hack” the source code of an imported library, that’s kind of an ugly solution really, I only did it because at the time I couldn’t find any better alternative, this tutorial is pretty old you see.

      Hope this helped!


      1. Debo

        Yes I have tried a lot. But not succeeded with YouTube + Photoswipe. I have seen Light gallery earlier. It does solve my problem. But it needs commercial license which is bothering me.

        Anyway if you find any solution from any source then plz do share.



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s