diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.html b/client/src/app/videos/+video-edit/shared/video-image.component.html
new file mode 100644
index 000000000..5d0624f8c
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.html
@@ -0,0 +1,16 @@
+
+
+
+ {{ inputLabel }}
+
+
+
(extensions: {{ videoImageExtensions }}, max size: {{ maxVideoImageSize | bytes }})
+
+
+
+
+
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.scss b/client/src/app/videos/+video-edit/shared/video-image.component.scss
new file mode 100644
index 000000000..c44d00b3c
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.scss
@@ -0,0 +1,26 @@
+@import '_variables';
+@import '_mixins';
+
+.root {
+ height: 150px;
+ display: flex;
+ align-items: center;
+
+ .button-file {
+ @include peertube-button-file(190px);
+ }
+
+ .image-constraints {
+ font-size: 13px;
+ }
+
+ .preview {
+ border: 2px solid grey;
+ border-radius: 4px;
+ margin-left: 50px;
+
+ &.no-image {
+ background-color: #ececec;
+ }
+ }
+}
diff --git a/client/src/app/videos/+video-edit/shared/video-image.component.ts b/client/src/app/videos/+video-edit/shared/video-image.component.ts
new file mode 100644
index 000000000..3f5705a92
--- /dev/null
+++ b/client/src/app/videos/+video-edit/shared/video-image.component.ts
@@ -0,0 +1,74 @@
+import { Component, forwardRef, Input } from '@angular/core'
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'
+import { ServerService } from '@app/core'
+import 'rxjs/add/observable/forkJoin'
+
+@Component({
+ selector: 'my-video-image',
+ styleUrls: [ './video-image.component.scss' ],
+ templateUrl: './video-image.component.html',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => VideoImageComponent),
+ multi: true
+ }
+ ]
+})
+
+export class VideoImageComponent implements ControlValueAccessor {
+ @Input() inputLabel: string
+ @Input() inputName: string
+ @Input() previewWidth: string
+ @Input() previewHeight: string
+
+ imageSrc: SafeResourceUrl
+
+ private file: Blob
+
+ constructor (
+ private sanitizer: DomSanitizer,
+ private serverService: ServerService
+ ) {}
+
+ get videoImageExtensions () {
+ return this.serverService.getConfig().video.image.extensions.join(',')
+ }
+
+ get maxVideoImageSize () {
+ return this.serverService.getConfig().video.image.size.max
+ }
+
+ fileChange (event: any) {
+ if (event.target.files && event.target.files.length) {
+ const [ file ] = event.target.files
+
+ this.file = file
+ this.propagateChange(this.file)
+ this.updatePreview()
+ }
+ }
+
+ propagateChange = (_: any) => { /* empty */ }
+
+ writeValue (file: any) {
+ this.file = file
+ this.updatePreview()
+ }
+
+ registerOnChange (fn: (_: any) => void) {
+ this.propagateChange = fn
+ }
+
+ registerOnTouched () {
+ // Unused
+ }
+
+ private updatePreview () {
+ if (this.file) {
+ const url = URL.createObjectURL(this.file)
+ this.imageSrc = this.sanitizer.bypassSecurityTrustResourceUrl(url)
+ }
+ }
+}