diff --git a/packages/person-profile/.gitignore b/packages/person-profile/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e62f92aa809c7345cb05914d7143b0322ab6d725
--- /dev/null
+++ b/packages/person-profile/.gitignore
@@ -0,0 +1,5 @@
+dist
+node_modules
+.idea
+npm-debug.log
+package-lock.json
diff --git a/packages/person-profile/.gitlab-ci.yml b/packages/person-profile/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9cadb871dc906edde0c952330b3a14f0b5767a58
--- /dev/null
+++ b/packages/person-profile/.gitlab-ci.yml
@@ -0,0 +1,20 @@
+image: debian:buster
+
+before_script:
+  - apt update
+  - apt install -y git
+  - "sed -i 's|git@gitlab.tugraz.at:VPU|../..|g' .gitmodules"
+  - git submodule sync
+  - git submodule update --init
+
+stages:
+  - test
+
+test:
+  stage: test
+  script:
+    - apt update
+    - apt install -y npm chromium
+    - npm install
+    - npm run build
+    - npm test
diff --git a/packages/person-profile/.gitmodules b/packages/person-profile/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..f997e57d8aaedcde29a6a64690d31268c7406e1f
--- /dev/null
+++ b/packages/person-profile/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "vendor/auth"]
+	path = vendor/auth
+	url = git@gitlab.tugraz.at:VPU/WebComponents/Auth.git
+[submodule "vendor/common"]
+	path = vendor/common
+	url = git@gitlab.tugraz.at:VPU/WebComponents/Common.git
diff --git a/packages/person-profile/assets/index.html b/packages/person-profile/assets/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..20f669bb214eafbf570a7b9902d87627e03f07e7
--- /dev/null
+++ b/packages/person-profile/assets/index.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <script type="module" src="bundle.js"></script>
+</head>
+
+<body>
+
+<vpu-person-profile-demo lang="de"></vpu-person-profile-demo>
+</body>
+</html>
diff --git a/packages/person-profile/i18next-scanner.config.js b/packages/person-profile/i18next-scanner.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c277798414be902846c6eb4f0e95a19750cda39
--- /dev/null
+++ b/packages/person-profile/i18next-scanner.config.js
@@ -0,0 +1,15 @@
+module.exports = {
+    input: [
+        'src/*.js',
+    ],
+    output: './',
+    options: {
+        debug: false,
+        removeUnusedKeys: true,
+        lngs: ['en','de'],
+        resource: {
+            loadPath: 'src/i18n/{{lng}}/{{ns}}.json',
+            savePath: 'src/i18n/{{lng}}/{{ns}}.json'
+        },
+    },
+}
diff --git a/packages/person-profile/karma.conf.js b/packages/person-profile/karma.conf.js
new file mode 100644
index 0000000000000000000000000000000000000000..6a646f5ec9ea5a6c50dd25dc6038dd94fff1bc43
--- /dev/null
+++ b/packages/person-profile/karma.conf.js
@@ -0,0 +1,23 @@
+// Trick to use the auto-downloaded puppeteer chrome binary
+process.env.CHROME_BIN = require('puppeteer').executablePath();
+
+module.exports = function(config) {
+  config.set({
+    basePath: 'dist',
+    frameworks: ['mocha', 'chai'],
+    files: [
+      {pattern: './*.js', included: true, watched: true, served: true, type: 'module'},
+      {pattern: './**/*', included: false, watched: true, served: true},
+    ],
+    autoWatch: true,
+    browsers: ['ChromeHeadlessNoSandbox'],
+    customLaunchers: {
+      ChromeHeadlessNoSandbox: {
+        base: 'ChromeHeadless',
+        flags: ['--no-sandbox']
+      }
+    },
+    singleRun: false,
+    logLevel: config.LOG_ERROR
+  });
+}
diff --git a/packages/person-profile/package.json b/packages/person-profile/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..a593a42645cda8fc64301f48362755b28b096cc2
--- /dev/null
+++ b/packages/person-profile/package.json
@@ -0,0 +1,46 @@
+{
+  "name": "vpu-person-profile",
+  "version": "1.0.0",
+  "main": "src/index.js",
+  "devDependencies": {
+    "karma": "^4.2.0",
+    "karma-chai": "^0.1.0",
+    "karma-chrome-launcher": "^3.0.0",
+    "karma-mocha": "^1.3.0",
+    "node-sass": "^4.12.0",
+    "puppeteer": "^1.19.0",
+    "mocha": "^6.2.0",
+    "chai": "^4.2.0",
+    "rollup": "^1.19.4",
+    "rollup-plugin-commonjs": "^10.0.2",
+    "rollup-plugin-consts": "^1.0.1",
+    "rollup-plugin-copy": "^3.1.0",
+    "rollup-plugin-node-resolve": "^5.2.0",
+    "rollup-plugin-serve": "^1.0.1",
+    "rollup-plugin-terser": "^5.1.1",
+    "rollup-plugin-json": "^4.0.0",
+    "rollup-plugin-multi-entry": "^2.1.0",
+    "rollup-plugin-url": "^2.2.2",
+    "i18next-scanner": "^2.10.2",
+    "vpu-auth": "file:./vendor/auth",
+    "vpu-common": "file:./vendor/common"
+  },
+  "dependencies": {
+    "bulma": "^0.7.5",
+    "lit-element": "^2.2.1"
+  },
+  "scripts": {
+    "clean": "rm dist/*",
+    "build": "npm run build-local",
+    "build-local": "rollup -c",
+    "build-dev": "rollup -c --environment BUILD:development",
+    "build-prod": "rollup -c --environment BUILD:production",
+    "build-demo": "rollup -c --environment BUILD:demo",
+    "build-test": "rollup -c --environment BUILD:test",
+    "i18next": "i18next-scanner",
+    "watch": "npm run watch-local",
+    "watch-local": "rollup -c --watch",
+    "watch-dev": "rollup -c --watch --environment BUILD:development",
+    "test": "npm run build-test && karma start --singleRun"
+  }
+}
diff --git a/packages/person-profile/rollup.config.js b/packages/person-profile/rollup.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..356c3ad25e2ea646e1b7d1a01f32775d05f3a093
--- /dev/null
+++ b/packages/person-profile/rollup.config.js
@@ -0,0 +1,53 @@
+import path from 'path';
+import resolve from 'rollup-plugin-node-resolve';
+import commonjs from 'rollup-plugin-commonjs';
+import copy from 'rollup-plugin-copy';
+import {terser} from "rollup-plugin-terser";
+import json from 'rollup-plugin-json';
+import serve from 'rollup-plugin-serve';
+import multiEntry from 'rollup-plugin-multi-entry';
+import url from "rollup-plugin-url"
+import consts from 'rollup-plugin-consts';
+
+const build = (typeof process.env.BUILD !== 'undefined') ? process.env.BUILD : 'local';
+console.log("build: " + build);
+
+export default {
+    input: (build != 'test') ? 'src/demo.js' : 'test/**/*.js',
+    output: {
+        file: 'dist/bundle.js',
+        format: 'esm'
+    },
+    plugins: [
+        multiEntry(),
+        consts({
+            environment: build,
+        }),
+        resolve({
+          customResolveOptions: {
+            // ignore node_modules from vendored packages
+            moduleDirectory: path.join(process.cwd(), 'node_modules')
+          }
+        }),
+        commonjs({
+            include: 'node_modules/**'
+        }),
+        url({
+            limit: 0,
+            include: [
+                "node_modules/bulma/**/*.css",
+                "node_modules/bulma/**/*.sass",
+            ],
+            emitFiles: true,
+            fileName: 'shared/[name].[hash][extname]'
+        }),
+        json(),
+        (build !== 'local' && build !== 'test') ? terser() : false,
+        copy({
+            targets: [
+                {src: 'assets/index.html', dest: 'dist'},
+            ],
+        }),
+        (process.env.ROLLUP_WATCH === 'true') ? serve({contentBase: 'dist', host: '127.0.0.1', port: 8002}) : false
+    ]
+};
diff --git a/packages/person-profile/src/demo.js b/packages/person-profile/src/demo.js
new file mode 100644
index 0000000000000000000000000000000000000000..01e0c43aba4106247207dbef4f2600c72933aa8e
--- /dev/null
+++ b/packages/person-profile/src/demo.js
@@ -0,0 +1,2 @@
+import 'vpu-auth';
+import './person-profile-demo.js';
diff --git a/packages/person-profile/src/i18n.js b/packages/person-profile/src/i18n.js
new file mode 100644
index 0000000000000000000000000000000000000000..02c88eb4fd48d17e453af6166addcd7583e92479
--- /dev/null
+++ b/packages/person-profile/src/i18n.js
@@ -0,0 +1,6 @@
+import {createInstance} from 'vpu-common/i18next.js';
+
+import de from './i18n/de/translation.json';
+import en from './i18n/en/translation.json';
+
+export const i18n = createInstance({en: en, de: de}, 'de', 'en');
\ No newline at end of file
diff --git a/packages/person-profile/src/i18n/de/translation.json b/packages/person-profile/src/i18n/de/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..2c63c0851048d8f7bff41ecf0f8cee05f52fd120
--- /dev/null
+++ b/packages/person-profile/src/i18n/de/translation.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/packages/person-profile/src/i18n/en/translation.json b/packages/person-profile/src/i18n/en/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..2c63c0851048d8f7bff41ecf0f8cee05f52fd120
--- /dev/null
+++ b/packages/person-profile/src/i18n/en/translation.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/packages/person-profile/src/index.js b/packages/person-profile/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac6be2e82f3c560e4d8f776409584e8ecbec8747
--- /dev/null
+++ b/packages/person-profile/src/index.js
@@ -0,0 +1 @@
+import './person-profile.js';
diff --git a/packages/person-profile/src/person-profile-demo.js b/packages/person-profile/src/person-profile-demo.js
new file mode 100644
index 0000000000000000000000000000000000000000..c42fa1a88d6ecedf3f645db8d864ab1a7f484606
--- /dev/null
+++ b/packages/person-profile/src/person-profile-demo.js
@@ -0,0 +1,51 @@
+import {i18n} from './i18n.js';
+import {html, LitElement} from 'lit-element';
+import './person-profile.js';
+import * as commonUtils from 'vpu-common/utils';
+import bulmaCSSPath from "bulma/css/bulma.min.css";
+import {getAssetURL} from "./utils";
+
+class PersonProfileDemo extends LitElement {
+    constructor() {
+        super();
+        this.lang = 'de';
+    }
+
+    static get properties() {
+        return {
+            lang: { type: String },
+        };
+    }
+
+    connectedCallback() {
+        super.connectedCallback();
+        i18n.changeLanguage(this.lang);
+
+        this.updateComplete.then(()=>{
+        });
+    }
+
+    render() {
+        const bulmaCSS = getAssetURL(bulmaCSSPath);
+        return html`
+            <link rel="stylesheet" href="${bulmaCSS}">
+
+            <header>
+                <div class="container">
+                    <vpu-auth lang="${this.lang}" client-id="${commonUtils.setting('keyCloakClientId')}" load-person remember-login style="float:right"></vpu-auth>
+                </div>
+            </header>
+
+            <section class="section">
+                <div class="container">
+                    <h1 class="title">Person-Profile-Demo</h1>
+                </div>
+                <div class="container">
+                    <vpu-person-profile lang="${this.lang}" entry-point-url="${commonUtils.getAPiUrl()}"></vpu-person-profile>
+                </div>
+            </section>
+        `;
+    }
+}
+
+commonUtils.defineCustomElement('vpu-person-profile-demo', PersonProfileDemo);
diff --git a/packages/person-profile/src/person-profile.js b/packages/person-profile/src/person-profile.js
new file mode 100644
index 0000000000000000000000000000000000000000..62f3bf54b3721fef1f716a186823a0ed37c19faa
--- /dev/null
+++ b/packages/person-profile/src/person-profile.js
@@ -0,0 +1,67 @@
+import {getAssetURL} from './utils.js';
+import JSONLD from 'vpu-common/jsonld';
+import {html} from 'lit-element';
+import {i18n} from './i18n.js';
+import VPULitElement from 'vpu-common/vpu-lit-element';
+import * as commonUtils from 'vpu-common/utils';
+import bulmaCSSPath from "bulma/css/bulma.min.css";
+
+
+class PersonProfile extends VPULitElement {
+
+    constructor() {
+        super();
+        this.lang = 'de';
+        this.entryPointUrl = commonUtils.getAPiUrl();
+        this.jsonld = null;
+        this.value = '';
+    }
+
+    static get properties() {
+        return {
+            lang: { type: String },
+            active: { type: Boolean, attribute: false },
+            entryPointUrl: { type: String, attribute: 'entry-point-url' },
+            value: { type: String },
+        };
+    }
+
+    connectedCallback() {
+        super.connectedCallback();
+
+        this.updateComplete.then(()=>{
+        });
+    }
+
+    update(changedProperties) {
+        changedProperties.forEach((oldValue, propName) => {
+            switch (propName) {
+                case "lang":
+                    i18n.changeLanguage(this.lang);
+                    break;
+                case "entryPointUrl":
+                    const that = this;
+
+                    JSONLD.initialize(this.entryPointUrl, function (jsonld) {
+                        that.jsonld = jsonld;
+                    }, {}, that.lang);
+                    break;
+            }
+        });
+
+        super.update(changedProperties);
+    }
+
+    render() {
+        const bulmaCSS = getAssetURL(bulmaCSSPath);
+        return html`
+            <link rel="stylesheet" href="${bulmaCSS}">
+            <style>
+            </style>
+
+            PROFILE
+        `;
+    }
+}
+
+commonUtils.defineCustomElement('vpu-person-profile', PersonProfile);
diff --git a/packages/person-profile/src/utils.js b/packages/person-profile/src/utils.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce1da2b82c374f7629d4179d1b0e21bfe6ed0e54
--- /dev/null
+++ b/packages/person-profile/src/utils.js
@@ -0,0 +1,9 @@
+export const getAssetURL = (path) => {
+    const elm = document.getElementById('vpu-library-shelving-wc-src');
+    if (!elm)
+        return path;
+    const url = elm.src;
+    // newer browsers only
+    //var url = import.meta.url;
+    return new URL(path, url).href;
+};
diff --git a/packages/person-profile/test/unit.js b/packages/person-profile/test/unit.js
new file mode 100644
index 0000000000000000000000000000000000000000..102c83a22448c02d0e1e6fb5f7b4c5c844dc252f
--- /dev/null
+++ b/packages/person-profile/test/unit.js
@@ -0,0 +1,20 @@
+import '../src/person-profile.js';
+import '../src/demo.js';
+
+describe('vpu-person-profile demo', () => {
+  let node;
+
+  beforeEach(async () => {
+    node = document.createElement('vpu-person-profile-demo');
+    document.body.appendChild(node);
+    await node.updateComplete;
+  });
+
+  afterEach(() => {
+    node.remove();
+  });
+
+  it('should render', () => {
+      expect(node).to.have.property('shadowRoot');
+  });
+});